728x90

1. 개념

1.1 Spring Batch 란?

  • 대용량 데이터반복적인 업무를 효율적으로 처리하기 위한 스프링 기반 배치 프레임워크

1.2 언제 사용하는가?

1.2.1 주기적으로 실행되는 작업이 있는 경우

  • ex) 정산

1.2.2 대용량 데이터를 처리해야 하는 경우

  • ex) 로그 분석

1.2.3 멀티 스레드, 분산 처리, 병렬 처리가 필요한 경우

  • ex) 매출 데이터 분석

1.2.4 재시도 혹은 재실행이 필요한 경우

  • ex) 외부 API에서 데이터를 가져와서 처리하는 경우

1.2.5 트랜잭션 관리가 필요한 경우

  • ex) 포인트 적립

 

1.3 왜 사용하는가?

1.3.1 일관된 아키텍처

  • 배치 작업에 필요한 공통 패턴과 템플릿을 미리 제공해 중복 코드 최소화

1.3.2 안정성 & 트랜잭션 관리

  • 실패 시 재시도, 재실행을 지원하며 트랜잭션 경계를 쉽게 설정 가능

1.3.3 손쉬운 모니터링 & 로깅

  • Job, Step 단위로 상태를 관리하여 문제 원인 파악이 쉬움

1.4 특징

1.4.1 잡(Job)과 스텝(Step)을 통한 구조적 설계

  • 작업 단계를 명확히 구분해 유지보수가 쉽고, 실패/재실행 시에도 어디서 문제가 발생했는지 확인이 용이

1.4.2 Chunk(청크) 지향 처리

  • 대량 데이터를 일정 덩어리(Chunk)로 나눠 읽고, 가공 후 한 번에 쓰는 방식

1.4.3 다양한 인터페이스 및 구현체 지원

  • ItemReader, ItemProcessor, ItemWriter 등 표준화된 인터페이스 제공
    • CSV, JDBC 등… 제공

1.4.4 에러 처리 및 재시도(Retry) 정책

  • 작업 도중 오류가 발생해도 재시도 횟수, 건너뛰기(Skip) 등을 설정 가능

2. 아키텍처

2.1 JobLauncher

  • 외부 (스케줄러 등)로 부터 배치를 실행 하라는 요청을 받아 시작
  • ❗햇갈리면 안되는 내용
    • Spring Batch VS Scheduler(or Quartz) 어떤걸 사용하는게 좋을까요??
      • 서로 다른 개념이니 비교하면 안된다!
      • Spring Batch는 대용량 처리!
      • Spring Scheduler는 특정한 시간에 등록된 작업을 자동으로 실행시키는 것!
  • Job과 파라미터 정보를 받아 Job을 실행하고, 그 결과를 반환

2.2 JobRepository

  • 배치 작업의 메타 데이터를 저장/관리하는 장소
  • JobStep의 실행 결과를 DB에 기록하여 재실행, 모니터링, 통계 분석 등 활용할 수 있도록 함

2.3 Job

  • 하나의 배치 프로세스 전체를 나타내는 상위 개념
  • 내부에 여러 Step을 가지고 있으며 각 Step을 순서대로 실행

2.4 Step

  • 배치 처리를 위한 실질적인 실행 단위

2.4.1 ItemReader

  • DB, 파일, 메시지 큐 등에서 데이터를 한 건씩(또는 여러 건 단위) 읽어 옴

2.4.2 ItemProcessor

  • 읽어온 데이터를 가공, 변환, 검증, 필터링 등 비즈니스 로직 수행

2.4.3 ItemWriter

  • 가공된 데이터를 최종적으로 저장(파일, DB 등)

2.5 ExecutionContext & JobExecution

  • ExecutionContext
    • Step에서의 중간 상태나 필요한 데이터를 임시로 저장하는 컨텍스트
  • JobExecution / StepExecution
    • 실행 시간, 처리 건수, 오류 발생 시점 등 세부 정보를 기록해 JobRepository에 보관
    • 재 시작 시 참조되어 어디까지 진행되었는지 확인 가능
반응형

'Study > Spring' 카테고리의 다른 글

[Spring] Spring Security + OAuth 2.0 소셜 로그인  (1) 2025.01.16
[Spring Boot] DB Lock  (0) 2025.01.10
[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
728x90

1. Spring Security

1.1 Spring Security 란?

  • 보안을 손쉽게 적용하기 위한 Spring Framework의 하위 프로젝트
  • 인증(Authentication)과 인가(Authorization)에 대한 다양한 기능을 제공
  • Filter 기반의 아키텍처로 구성되어 있어, 다양한 커스터마이징이 가능

1.2 Spring Security 인증 및 인가 흐름

2. OAuth 2.0

2.1 OAuth2.0 이란?

  • Open + Authorization (개방형 인가)
  • 웹 및 애플리케이션 인증 및 권한 부여를 위한 개방형 표준 프로토콜
  • 외부 Resoure에 접근하기 위해 사용

2.2 기본 용어

2.2.1 서버 및 클라이언트

  • Resource Owner(사용자)
  • Client(애플리케이션)
  • Authorization Server(인증 서버, 토큰 발급)
  • Resource Server(API 서버, 자원을 실제로 소유)

2.2.2 기타

  • Authorization Code (인증 코드)
  • Scope (범위)

2.3 인증 방식

2.3.1 Authrozation Code Grant

  • 서버 기반 애플리케이션(Server-side Application)에서 가장 많이 사용되는 인증 방식
  • 사용자에게서 직접 자격 증명 정보를 받지 않고, “인가 코드(Authorization Code)”를 발급받아 이를 이용해 액세스 토큰(Access Token)을 얻는 구조

2.3.2 Implicit Grant

  • 브라우저에서 동작하는 자바스크립트 기반 애플리케이션과 같이 클라이언트 비밀(Client Secret)을 안전하게 보관하기 어려운 환경에서 사용하던 방식

2.3.3 Password Credentials Grant

  • 사용자가 클라이언트에 직접 아이디/비밀번호 등의 자격 증명을 입력하고, 클라이언트가 이를 권한 서버에 전달하여 인증 및 액세스 토큰을 발급받는 방식

2.3.4 Client Credentials Grant

  • 서버 대 서버 혹은 백엔드 서비스 간 통신과 같이, 사용자 대신 애플리케이션 자체가 리소스에 접근해야 할 때 사용하는 방식

2.4 통합 흐름

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] Spring Batch  (0) 2025.01.23
[Spring Boot] DB Lock  (0) 2025.01.10
[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
728x90

1. 개념

1.1 DB Lock 이란?

  • DB Lock은 여러 트랜잭션이 동시에 같은 자원에 접근할 때 데이터 무결성(정합성)을 보장하기 위해 사용되는 메커니즘
  • 쉽게 말해, 어떤 사용자가 데이터를 사용하고 있는 동안에는 다른 사용자가 그 데이터를 동시에 수정하면 안 되기 때문에, ‘잠금’을 걸어서 충돌(Conflict)을 방지

1.2 왜 Lock이 필요한가?

  • 여러 사용자가 동시에 DB를 사용하는 경우 같은 데이터를 동시에 수정하려고 시도하는 경우 충돌 제어 및 데이터의 일관성을 보장하기 위해

1.3 언제 Lock을 사용하는가?

  • 사용해야 하는 경우!
    • 동시에 읽기/쓰기가 빈번하게 일어나는 중요한 테이블에 대해, 데이터 무결성을 엄격히 보장해야 할 때.
    • 은행 이체, 재고 관리, 주문 처리와 같이 동시에 발생하면 안 되는 시나리오를 제어할 때.
  • 주의해야할 점!
    • 불필요한 락은 성능 저하를 유발할 수 있으므로, 적절한 수준에서 사용하는 것이 중요
    • DeadLock!!
      • DeadLock발생 여부를 확인해야함!

1.4 Lock의 생명주기

1.4.1 트랜잭션 시작!

  • DB와의 트랜잭션을 시작
  • 아직 특정 자원에 대한 락이 획득된 상태는 🙅

1.4.2 Lock 획득

  1. 쿼리 요청
  2. Lock 체크
  3. 대기 혹은 Lock 획득

1.4.3 트랜잭션 종료

  1. 트랜잭션 커밋 혹은 롤백
  2. 락 해제

2. Lock 종류

2.1 낙관적 락 (충돌이 발생하지 않을 것이라고 가정)

  • 데이터베이스의 락을 사용하는 것이 아닌 application 레벨에서 버전 관리
  • 특정 작업을 수행하기 전에 별도의 락(lock)을 걸지 않고, 작업 완료 시점에서 데이터의 변경 여부를 확인하여 충돌 여부를 판단 (@Version 활용)
  • 데이터 충돌이 거의 없을것이라고 가정한 경우 사용
    • LockModeType.OPTIMISTIC로 적용
  • 충돌 시 ObjectOptimisticLockingFailureException 발생

2.2 비관적 락 (충돌이 자주 발생할 것이라고 가정)

2.2.1 공유락(Shared Lock, S Lock)

  • 여러 트랜잭션이 동시에 데이터를 읽기할 수 있지만, 쓰기 하려면 공유락을 해제하고 배타락으로 변경
  • 다른 트랜잭션이 쓰기하려 하면 대기 상태
    • LockModeType.PESSIMISTIC_READ로 적용

2.2.2 배타락(Exclusive Lock, X Lock)

  • 오직 한 트랜잭션만 해당 데이터를 읽거나 쓸 수 있음
  • 다른 트랜잭션이 접근하려 하면 대기 상태
    • LockModeType.PESSIMISTIC_WRITE로 적용

2.3 분산 락

  • 여러 인스턴스나 분산 환경에서 락을 설정
  • 데이터베이스에 직접 Lock을 걸지 않고, 외부에서 권한을 받아 처리
  • Redis, **Zookeeper 등... 을 활용하여 적용**

 

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] Spring Batch  (0) 2025.01.23
[Spring] Spring Security + OAuth 2.0 소셜 로그인  (1) 2025.01.16
[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
728x90

1. QueryDSL(Query Domain-Specific Language)

1.1 💡QueryDSL 이란?

  • SQL, JPQL 같은 쿼리를 Java 코드로 작성할 수 있게 해주는 타입 안전한 ORM 기반 쿼리 빌더
  • SQLJava 코드로 작성하면서, 가독성과 유지보수성을 대폭 개선할 수 있는 도구

1.2 ☝️ 왜 사용해야할까

1.2.1 SQL/JPQL의 한계 해결

  • 문법 오류 - 쿼리의 오류를 런타임에서만 확인 가능
  • 필드 이름 변경 - 엔티티 필드가 변경되면 문자열 기반 쿼리에서 이를 수동으로 수정해야 함.
  • 동적 쿼리 작성의 어려움 - 조건에 따라 쿼리를 유연하게 작성하려면 코드 복잡도 향상
  • 코드 비교
// JPQL
// 필터가 많아질수록 코드가 복잡
public List<User> findUsersDynamicJPQL(EntityManager em, Integer age, String name) {
    StringBuilder jpql = new StringBuilder("SELECT u FROM User u WHERE 1=1");

    if (age != null) {
        jpql.append(" AND u.age > :age");
    }
    if (name != null) {
        jpql.append(" AND u.name = :name");
    }

    TypedQuery<User> query = em.createQuery(jpql.toString(), User.class);
    if (age != null) query.setParameter("age", age);
    if (name != null) query.setParameter("name", name);

    return query.getResultList();
}


// QueryDSL
// BooleanBuilder 사용
public List<User> findUsersDynamicQueryDSL(JPAQueryFactory queryFactory, Integer age, String name) {
    QUser user = QUser.user;

    BooleanBuilder builder = new BooleanBuilder(); // 동적 조건 빌더
    if (age != null) {
        builder.and(user.age.gt(age));
    }
    if (name != null) {
        builder.and(user.name.eq(name));
    }

    return queryFactory
            .selectFrom(user)
            .where(builder) // 동적 조건 적용
            .fetch();
}

1.2.2 복잡한 쿼리 요구사항 처리

  • 조인 - 여러 테이블 데이터를 조합해서 처리.
  • 서브쿼리 - 쿼리 안에 또 다른 쿼리를 포함.
  • 그룹핑 및 집계 - 데이터를 그룹화하거나 통계 처리.

1.2.3 장/단점

  • 장점
    • 타입 안전한 쿼리 작성
    • 가독성 UP, 유지보수성 UP
    • 동적 쿼리 작성의 유연함
    • 다양한 쿼리 지원
  • 단점
    • 초기 세팅의 복잡성, 의존성 관리
    • Q 클래스 생성 필요
    • 러닝 커브

1.3 💁 언제 사용해야할까?

1.3.1 🙆 사용해야하는 경우

  • 동적 쿼리가 많은 프로젝트
  • 복잡한 쿼리를 자주 작성해야 하는 대규모 애플리케이션
  • 타입 안전성과 코드 가독성을 중시하는 팀 환경

1.3.2 🙅 사용하지 말아야 하는 경우

  • 프로젝트가 간단한 CRUD 중심인 경우
  • 네이티브 SQL을 주로 사용하는 환경

1.4 ✈️ 대안

1.4.1 JPA Criteria API

  • JPA 표준 스펙에 포함된 쿼리 빌더 API

✅ 장점

  • 표준 API: JPA 구현체(Hibernate 등)와 상관없이 동작.
  • 별도의 외부 라이브러리 없이 사용 가능.

❌ 단점

  • 가독성 부족: 코드가 복잡하고 장황해지기 쉬움.
  • 유지보수가 어려움.
  • 예제 코드
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);

Predicate agePredicate = cb.gt(user.get("age"), 18);
Predicate statusPredicate = cb.equal(user.get("status"), "ACTIVE");

query.select(user).where(cb.and(agePredicate, statusPredicate));
List<User> result = em.createQuery(query).getResultList();

1.4.2 Spring Data JPA Specification

  • Spring Data JPA에서 제공하는 Specification 인터페이스를 활용하면 동적 쿼리 구현

✅ 장점

  • Spring Data JPA와 통합: Spring 프로젝트와 자연스럽게 연동.
  • 동적 조건 작성이 비교적 간단.

❌ 단점

  • QueryDSL에 비해 표현력이 제한적.
  • 복잡한 쿼리 작성에는 적합하지 않음.
  • 예제코드
public class UserSpecifications {
    public static Specification<User> hasAgeGreaterThan(int age) {
        return (root, query, cb) -> cb.gt(root.get("age"), age);
    }

    public static Specification<User> hasStatus(String status) {
        return (root, query, cb) -> cb.equal(root.get("status"), status);
    }
}

// 사용
Specification<User> spec = Specification.where(UserSpecifications.hasAgeGreaterThan(18))
                                         .and(UserSpecifications.hasStatus("ACTIVE"));

List<User> users = userRepository.findAll(spec);

1.4.3 네이티브 SQL

✅ 장점

  • 유연성: 데이터베이스에 특화된 쿼리를 작성 가능
  • 성능 최적화에 유리

❌ 단점

  • 타입 안전성 부족: 문자열 기반으로 작성
  • 데이터베이스 종속적 → 이식성이 낮음
  • 유지보수가 어려움
  • 예제 코드
@Query(value = "SELECT * FROM users WHERE age > :age AND status = :status", nativeQuery = true)
List<User> findUsersNative(@Param("age") int age, @Param("status") String status);

1.4.4 jOOQ (Java Object Oriented Querying)

  • jOOQ는 SQL을 Java 코드로 작성하면서도, 데이터베이스와 밀접하게 통합하여 고급 기능을 사용할 수 있는 강력한 도구

✅ 장점

  • SQL 친화적 설계: SQL의 고급 기능(윈도우 함수, CTE 등)을 Java 코드로 직접 활용 가능.
  • 타입 안전성: SQL 컬럼과 Java 필드 간의 타입을 컴파일 타임에 검증.
  • DBMS 특화 기능 지원: 데이터베이스에 따라 최적화된 SQL 생성 가능.
  • 코드 자동 생성: 테이블 스키마 기반으로 Java 코드를 생성하여, 변경 사항을 자동 반영.

❌ 단점

  • SQL 종속성: 데이터베이스 변경 시 쿼리 코드 수정이 필요.
  • 학습 난이도: SQL에 익숙하지 않은 개발자에게는 진입 장벽이 높음.
  • ORM 부족: 객체 지향적 추상화가 부족하고, SQL 중심의 설계에 초점.
  • 예제 코드
// 동적 조건 추가
public List<User> findUsersDynamicJooq(DSLContext context, Integer age, String name) {
    Condition condition = DSL.trueCondition();

    if (age != null) {
        condition = condition.and(USER.AGE.gt(age));
    }
    if (name != null) {
        condition = condition.and(USER.NAME.eq(name));
    }

    return context.selectFrom(USER)
                  .where(condition)
                  .fetchInto(User.class);
}

 

 

라이브러리 특징 장점 단점
QueryDSL - 타입 안전한 동적 쿼리 작성을 지원
- Q클래스를 통해 메서드 체인 방식으로 쿼리를 작성
- 타입 안전성(Type Safety) 보장
- 가독성 높은 코드 작성 가능
- IDE 자동완성 지원
- 초기 설정 복잡
- Q클래스 생성 필요
Spring Data JPA Specifications - JPA의 Criteria API를 기반으로 동적 쿼리를 작성
- Specification 인터페이스를 사용
- 추가 라이브러리 없이 Spring Data JPA와 자연스러운 통합
- 동적 쿼리 작성 가능
- 코드가 장황하고 가독성이 떨어질 수 있음
JOOQ - SQL 빌더 라이브러리로, SQL 문법을 코드로 표현
- 고급 SQL 기능을 지원
- SQL 문법을 코드로 직관적으로 표현 가능
- 복잡한 쿼리와 고급 SQL 기능 활용 가능
- JPA와 통합하려면 추가 설정 필요
Criteria API (JPA 기본) - JPA 표준 사양
- 메서드 체인 방식을 통해 동적 쿼리를 작성
- 모든 JPA 구현체에서 사용 가능
- 추가 의존성 필요 없음
- 코드가 복잡하고 가독성이 떨어짐
Native QSL - SQL 문법그대로 쿼리 작성 - 데이터베이스에 특화된 쿼리를 작성 가능
- 성능 최적화에 유리
- 데이터베이스 종속적 → 이식성이 낮음
- 유지보수가 어려움

 

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] Spring Security + OAuth 2.0 소셜 로그인  (1) 2025.01.16
[Spring Boot] DB Lock  (0) 2025.01.10
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
728x90

(1) @InitBinder 이란?

  • WebDataBinder를 초기화 하는 메소드를 지정할 수 있는 설정을 제공합니다.
  • 특정 컨트롤러에서 바인딩 또는 검증 설정을 변경하고 싶을 때 사용합니다.
  • 모든 요청 전 InitBinder를 선언한 메소드가 실행됩니다.

(2) 사용법

  • Controller
    @RestController
    public class MemberController {
    
    	private final MemberService memberService;
    	private final MemberLoginRequestValidator memberLoginRequestValidator;
        
        @Autowired
        public MemberController(MemberService memberService, MemberLoginRequestValidator memberLoginRequestValidator){
        	this.memberService = memberService;
            this.memberLoginRequestValidator = memberLoginRequestValidator;
        }
    
        //memberLoginRequestDto 객체로 요청이 들어올 경우 memberLoginRequestValidator를 우선 실행
        @InitBinder("memberLoginRequestDto")
        public void loginBinder(WebDataBinder webDataBinder){
            webDataBinder.addValidators(memberLoginRequestValidator);
        }
        
        //로그인
        @PostMapping("/api/member/login")
        public ResponseEntity<Map<String,String>> memberLogin(@Valid @RequestBody MemberLoginRequestDto memberLoginRequestDto, HttpServletResponse httpServletResponse){
    
            return ResponseEntity.ok(memberService.memberLogin(memberLoginRequestDto, httpServletResponse));
        }
    }
  • Validator
    @Component
    public class MemberLoginRequestValidator implements Validator {
    
        private final MemberRepository memberRepository;
        private final PasswordEncoder passwordEncoder;
    
        public MemberLoginRequestValidator(MemberRepository memberRepository, PasswordEncoder passwordEncoder){
            this.memberRepository = memberRepository;
            this.passwordEncoder = passwordEncoder;
        }
    
        @Override
        public boolean supports(Class<?> clazz){
            return clazz.isAssignableFrom(MemberLoginRequestDto.class);
        }
    
        @Override
        public void validate(Object object, Errors errors){
            MemberLoginRequestDto memberLoginRequestDto = (MemberLoginRequestDto) object;
    
            if(!memberRepository.existsById(memberLoginRequestDto.getEmail())){
                errors.rejectValue("email", "invalid.email", "존재하지 않는 email입니다.");
            } else {
                MemberEntity memberEntity = memberRepository.getById(memberLoginRequestDto.getEmail());
                if(!passwordEncoder.matches(memberLoginRequestDto.getPassword(), memberEntity.getPassword())) {
                    errors.rejectValue("password","invalid.password", "패스워드가 일치하지 않습니다.");
                }
            }
        }



반응형

'Study > Spring' 카테고리의 다른 글

[Spring Boot] DB Lock  (0) 2025.01.10
[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
[Spring Boot] Spring 프레임워크  (2) 2021.06.26
728x90

(1) @Data 어노테이션이란?

  • @Data = @toString + @getter + @setter + @RequiredArgsConstructor + @EqualsAndHashCode
  • @toString - toString() 메서드를 생성한다. @ToString(exclude = {"제외값"})으로 제외시키고 싶은 값을 설정할 수 있다.
  • @getter/setter - getter(), setter() 메서드를 생성한다.
  • @RequiredArgsConstructor - 초기화 되지 않은 모든 final 필드, @NonNull과 같이 제약조건이 설정되어있는 모든 필드들에 대한 생성자를 자동으로 생성한다.
  • @EqualsAndHashCode - equals(), hashCode() 메서드를 생성한다.

(2) 문제점

  • @RequiredArgsConstructor 어노테이션으로 문제가 발생할 수 있다. 예를 들어 인해 두 개의 타입 인스턴스 멤버를 선언한 상황에서 개발자가 선언된 인스턴스 멤버의 순서를 바꾸면, 개발자도 인식하지 못하는 사이에 lombok이 생성자의 파라미터 순서를 필드 선언 순서에 따라 변형하게 된다. 이때, IDE가 제공해주는 리팩토링은 전혀 동작하지 않고, 두 필드가 동일 타입이기 때문에 기존 소스에서도 오류가 발생하지 않아 아무런 문제없이 동작하는 것으로 보이지만, 실제로 입력된 값이 바뀌어 들어가는 상황이 발생한다.

출처 - https://velog.io/@leyuri/Spring-boot-JPA-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-Entity-Table-Column-Id-Lombok

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
[Spring Boot] Spring 프레임워크  (2) 2021.06.26
[Spring Boot] 유효성 검사(Validation)  (0) 2021.04.11
728x90

(1) 영속성 컨텍스트 (Persistence Context)란?

  • 영속성 컨텍스트는 엔티티를 영구 저장하는 환경입니다.
  • 영속성 컨텍스트는 눈에 보이지 않는 논리적인 개념입니다.

(2) 영속성 컨텍스트 (Persistence Context) 특징

  • @Id 어노테이션을 통해 엔티티를 식별합니다.
  • 1차 캐시가 있습니다. Entity가 DB에 저장되기 전에 사용되는 공간인데, 반대로 DB를 조회하더라도 1차캐시에 저장하게 됩니다. 따라서 같은 Entity를 읽고자 할 때 빠른 읽기 기능을 제공하고 부하를 줄여줍니다. DB에 저장할 때 역시 1차 캐시에 저장됨으로써 중간에 수정할 사항이 있다면 UPDATE쿼리를 사용하지 않고도 INSERT쿼리만으로 바로 저장할 수 있습니다.
  • 위 처럼 캐시에 있는 것과 실제 DB에 존재하는 것이 동일하기 때문에 객체의 동일성을 보장합니다.
  • 트랜잭션을 지원하는 쓰기지연 기능이있습니다. 즉 값을 변경하자마자 바로 DB에 반영하는 것이 아니라, 영속성 컨텍스트 내부의 SQL 저장소에 생성 쿼리를 저장 해둡니다. 이 후 commit을 하게 되면 저장해두었던 쿼리를 데이터베이스에 보냅니다.
  • 엔티티 매니저는 find(조회), persist(추가), remove(삭제)만 있으며 update() 메서드는 존재하지 않는데 그 이유는 변경 감지(dirty checking)를 하기 때문입니다. 변경 감지가 가능한 이유는 1차 캐시에 최초로 저장될 때 그 상태를 스냅샷을 해 두었다가 영속성 컨텍스트와 DB사이의 동기화가 이루어지는 flush 시점에서 스냅샷과 현재 엔티티의 상태를 비교하여 엔티티가 변경되었다면 UPDATE 쿼리를 실행합니다.

참조 - http://blog.neonkid.xyz/233, https://victorydntmd.tistory.com/207

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring Boot] Spring 프레임워크  (2) 2021.06.26
[Spring Boot] 유효성 검사(Validation)  (0) 2021.04.11
728x90

(1) Spring 프레임워크란?

  • 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서 엔터프라이즈급 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 경량화된 솔루션입니다. Spirng Framework는 경량 컨테이너로 자바 객체를 담고 직접 관리합니다. 객체의 생성 및 소멸 그리고 라이프 사이클을관리하며 언제든 Spring 컨테이너로 부터 필요한 객체를 가져와 사용할 수 있습니다. 이는 Spirng이 IOC 기반의 Framework임을 의미합니다.
  • 엔터프라이즈급 개발이란 뜻대로만 풀이하면 기업을 대상으로 하는 개발이라는 말입니다. 즉, 대규모 데이터 처리와 트랜잭션이 동시에 여러 사용자로 부터 행해지는 매우 큰 규모의 환경을 엔터프라이즈 환경이라 일컫습니다.

(2) Spring 프레임워크의 주요 특징

    • IOC(제어의 반전)
      • 스프링의 가장 중요하고 핵심적인 기능으로서 자바의 반영(reflection)을 이용해서 객체의 생명주기를 관리하고 의존성 주입(Dependency Injection)을 통해 각 계층이나 서비스들간의 의존성을 맞춰준다. 이러한 기능들은 주로 환경설정을 담당하는 XML 파일에 의해 설정되고 수행된다.
    • AOP(관점 지향 프로그래밍)
      • 대부분 소프트웨어 개발 프로세스에서 사용하는 방법은 OOP(Object Oriented Programming) 입니다. OOP는 객체지향 원칙에 따라 관심사가 같은 데이터를 한곳에 모아 분리하고 낮은 결합도를 갖게하여 독립적이고 유연한 모듈로 캡슐화를 하는 것을 일컫습니다. 하지만 이러한 과정 중 중복된 코드들이 많아지고 가독성, 확장성, 유지보수성을 떨어 뜨립니다.
      • AOP에서는 핵심기능과 공통기능을 분리시켜 핵심 로직에 영향을 끼치지 않게 공통기능을 끼워 넣는 개발 형태 이며 이렇게 개발함에 따라 무분별하게 중복되는 코드를 한 곳에 모아 중복 되는 코드를 제거 할 수 있어지고 공통기능을 한 곳에 보관함으로써 공통 기능 하나의 수정으로 모든 핵심기능들의 공통기능을 수정 할 수 있어 효율적인 유지보수가 가능하며 재활용성이 극대됩니다. 물론 AOP로 만들 수 있는 기능은 OOP로 구현 할 수 있는 기능이지만 Spring에서는 AOP를 편리하게 사용 할 수 있도록 이를 지원하고 있습니다.
    • POJO(Plain Old Java Object)
      • 이전 EJB(Enterprise JavaBeans)는 확장 가능한 재사용이 가능한 로직을 개발하기 위해 사용 되었었는데 EJB는 한가지 기능을 위해 불필요한 복잡한 로직이 과도하게 들어가는 단점이 있었습니다. 그래서 다시 조명을 받은게 POJO입니다. POJO는 gettet/setter를 가진 단순 자바 오브젝트로 정의를 하고 있습니다. 이러한 단순 오브젝트는 의존성이 없고 추후 테스트 및 유지보수가 편리한 유연성의 장점을 가집니다. 이러한 장점들로 인해 객체지향적인 다양한 설계와 구현이 가능해지고 POJO의 기반의 Framework가 조명을 받고 있습니다.
    • MVC 패턴
      • 스프링은 웹 프로그램밍 개발 시 거의 표준적인 방식인 Spring MVC라 불리는 모델-뷰-컨트롤러(MVC) 패턴을 사용한다. DispatcherServlet이 Controller 역할을 담당하여 각종 요청을 적절한 서비스에 분산시켜주며 이를 각 서비스들이 처리를 하여 결과를 생성하고 그 결과는 다양한 형식의 View 서비스들로 화면에 표시될 수 있다.
      • Model
        • Model에서는 데이터처리를 담당하는 부분입니다. Model부분은 Serivce영역과 DAO영역으로 나누어지게 되고 여기서 중요한 것은 Service 부분은 불필요하게 HTTP통신을 하지 않아야하고 request나 response와 같은 객체를 매개변수로 받아선 안된다. 또한 Model 부분의 Service는 view에 종속적인 코드가 없어야 하고 View 부분이 변경되더라도 Service 부분은 그대로 재사용 할 수 있어야 한다.Model에서는 View와 Controller 어떠한 정보도 가지고 있어서는 안된다.
      • View
        • View는 사용자 Interface를 담당하며 사용자에게 보여지는 부분입니다. View는 Controller를 통해 모델에 데이터에 대한 시각화를 담당하며 View는 자신이 요청을 보낼 Controller의 정보만 알고 있어야 하는 것이 핵심이다. Model이 가지고 있는 정보를 저장해서는 안되며 Model, Controller에 구성 요소를 알아서는 안된다.
      • Controller
        • Controller에서는 View에 받은 요청을 가공하여 Model(Service 영역)에 이를 전달한다. 또한 Model로 부터 받은 결과를 View로 넘겨주는 역할을 합니다. Controller에서는 모든 요청 에러와 모델 에러를 처리하며 View와 Controller에 정보를 알고 있어야한다. Model과 View의 정보에 대해 알고 있어야한다.
    • 트랜잭션 지원
      • 데이터베이스를 연동하여 사용할 때 발생할 수 있는 문제를 해결하기 위해 트랜잭션 처리를 해주어야 한다. 스프링에서는 이러한 트랜잭션 처리를 어노태이션이나 xml로 설정할 수 있도록 지원해준다.
    • 일괄처리
      • 스프링은 특정 시간대에 실행하거나 대용량의 자료를 처리하는데 쓰이는 일괄 처리(Batch Processing)을 지원하는 배치 프레임워크를 제공한다. 기본적으로 스프링 배치는 Quartz 기반으로 동작한다.

참조 - https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%94%84%EB%A7%81_%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC, https://khj93.tistory.com/entry/Spring-Spring-Framework%EB%9E%80-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC

  •  
반응형

'Study > Spring' 카테고리의 다른 글

[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
[Spring Boot] 유효성 검사(Validation)  (0) 2021.04.11
728x90

(1) 유효성 검사(Validation) 란?

  • 어떤 데이터의 값이 유효한지, 타당한지 확인하는 것을 의미합니다.
  • 보안적인 측면에서 올바르지 않는 데이터가 서버로 전송되거나 DB에 전송되지 않도록 하는 것입니다.

(2) 관련 어노테이션

  • JSR-303 Validator
    • @AssertFalse - 거짓인가?
    • @AssertTrue - 참인가?
    • @Max - 지정 값 이하인가?
    • @Min - 지정 값 이상인가?
    • @DecimalMax - 지정 값 이하 실수인가?
    • @DecimalMin - 지정 값 이상 실수인가?
    • @NotNull - Null이 아닌가?
    • @Null - Null 인가?
    • @Digits (integer=, fraction=) - 대상 수가 지정된 정수, 소수 자리 수 이내인가?
    • @Pattern(regex=,flag=) - 정규식을 만족 하는가?
    • @Future - 미래날짜인가?
    • @Past - 과거 날짜인가?
    • @Size(min=, max=) - 문자열, 배열 등의 크기가 지정크기를 만족 하는가?
  • Hibernate
    • @NotEmpty - Empty 값이 아닌가?
    • @Email - 이메일 형식인가?
    • @URL - URL 형식인가?
    • @Length(min=, max=) - 문자열의 길이가 min과 max 사이인가?
    • @Range(min=, max=) - 숫자 범위가 min과 max 사이인가?

(3) 사용법

  • build.gradle
dependencies {
	compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    
    // https://mvnrepository.com/artifact/org.json/json
    implementation group: 'org.json', name: 'json', version: '20160810'
    
	implementation 'org.springframework.boot:spring-boot-starter-validation:2.3.3.RELEASE'
}
  • DTO
    @NotNull
    @NotBlank(message = "이메일 입력은 필수입니다.")
    @Email(message = "이메일 형식으로 입력해 주세요.")
    private String email;

    @NotNull
    @NotBlank(message = "비밀번호 입력은 필수입니다.")
    private String password;
  • Controller
    //회원 가입
    @PostMapping("/api/user/signup")
    public Object registerUsers(@Valid @RequestBody UserSignupRequestDto userSignupRequestDto){

    }
  • ErrorCode
import lombok.Getter;

public enum ErrorCode {

    NOT_NULL("ERROR_CODE_NOT_NULL","필수값이 누락되었습니다.")
    , MIN_VALUE("ERROR_CODE_MIN_VALUE", "최소값보다 커야 합니다.")
    , PATTERN("ERROR_CODE_PATTERN","값 형식이 다릅니다.")
    , NOT_BLANK("ERROR_CODE_NOT_BLANK","필수값이 누락되었습니다.")
    , EMAIL("ERROR_CODE_EMAIL","이메일 형식이 아닙니다.")
    ;

    @Getter
    private String code;

    @Getter
    private String description;

    ErrorCode(String code, String description) {
        this.code = code;
        this.description = description;
    }
}
  • ErrorResponse
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class ErrorResponse {

    private String code;

    private String description;

    private String errorMessage;

    public ErrorResponse(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public ErrorResponse(String code, String description, String errorMessage) {
        this.code = code;
        this.description = description;
        this.errorMessage = errorMessage;
    }
}
  • ExceptionController
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

@Slf4j
@ControllerAdvice
public class ExceptionController {

    /**
     * @valid  유효성체크에 통과하지 못하면  MethodArgumentNotValidException 이 발생한다.
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> methodValidException(MethodArgumentNotValidException e, HttpServletRequest request){
        log.warn("MethodArgumentNotValidException 발생!!! url:{}, trace:{}",request.getRequestURI(), e.getStackTrace());
        ErrorResponse errorResponse = makeErrorResponse(e.getBindingResult());
        return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    private ErrorResponse makeErrorResponse(BindingResult bindingResult){
        String code = "";
        String description = "";
        String errorMessage = "";

        //에러가 있다면
        if(bindingResult.hasErrors()){
            //DTO에 설정한 meaasge값을 가져온다
            errorMessage = bindingResult.getFieldError().getDefaultMessage();

            //DTO에 유효성체크를 걸어놓은 어노테이션명을 가져온다.
            String bindResultCode = bindingResult.getFieldError().getCode();

//            switch (bindResultCode){
            switch (Objects.requireNonNull(bindResultCode)){
                case "NotNull":
                    code = ErrorCode.NOT_NULL.getCode();
                    description = ErrorCode.NOT_NULL.getDescription();
                    break;
                case "NotBlank":
                    code = ErrorCode.NOT_BLANK.getCode();
                    description = ErrorCode.NOT_BLANK.getDescription();
                    break;
                case "Min":
                    code = ErrorCode.MIN_VALUE.getCode();
                    description = ErrorCode.MIN_VALUE.getDescription();
                    break;
                case "Pattern":
                    code = ErrorCode.PATTERN.getCode();
                    description = ErrorCode.PATTERN.getDescription();
                    break;
                case "Email":
                    code = ErrorCode.EMAIL.getCode();
                    description = ErrorCode.EMAIL.getDescription();
                    break;
            }
        }

        return new ErrorResponse(code, description, errorMessage);
    }
}

 

  • Controller에서 @Valid 어노테이션이 붙은 값이 넘어왔을때 DTO에서 지정해준 조건에 맞지 않는다면 ExceptionController의 makeErrorResponse 함수가 작동하게 되고, 프론트엔드로 ErrorCode에서 지정해준 값과 DTO에 지정해준 Message가 Response Body에 담겨 프론트엔드로 응답하게 됩니다.

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] QueryDSL-JPA  (0) 2024.12.30
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
[Spring Boot] Spring 프레임워크  (2) 2021.06.26

+ Recent posts