ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • bank app 9 - 회원가입(트랜잭션처리)
    spring boot 2023. 4. 18. 11:50

     

     

    UserController

    /**
    	 * 회원 가입 처리 
    	 * @param signUpFormDto
    	 * @return 리다이렉트 로그인 페이지
    	 */
    @PostMapping("/sign-up")
    public String signUpProc(SignUpFormDto signUpFormDto) {
    	
    	// 1. 유효성 검사  
    	if(signUpFormDto.getUsername() == null 
    			|| signUpFormDto.getUsername().isEmpty()) {
    		throw new CustomRestfullException("username을 입력해주세요", HttpStatus.BAD_REQUEST);
    	}
    	if(signUpFormDto.getPassword() == null 
    			|| signUpFormDto.getPassword().isEmpty()) {
    		throw new CustomRestfullException("password을 입력해주세요", HttpStatus.BAD_REQUEST);
    	}
    	if(signUpFormDto.getFullname() == null 
    			|| signUpFormDto.getFullname().isEmpty()) {
    		throw new CustomRestfullException("fullname을 입력해주세요", HttpStatus.BAD_REQUEST);
    	} 
    	// 서비스 호출 
    	userService.signUp(signUpFormDto);
    	
    	return "redirect:/user/sign-in";
    }

     

     

    package com.tenco.bank.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.tenco.bank.dto.SignUpFormDto;
    import com.tenco.bank.handler.exception.CustomRestfullException;
    import com.tenco.bank.repository.interfaces.UserRepository;
    
    @Service	//Ioc 대상 // 싱글톤 패턴으로 관리
    public class UserService {
    
    	@Autowired // DI 처리 (객체 생성시 의존 주입 처리)
    	private UserRepository userRepository;
    	
    	@Transactional 
    	// 메서드 호출이 시작될 때 트랜잭션에 시작
    	// 메서드 종료시 트랜젝션 종료 (commit)
    	public void singUp(SignUpFormDto signUpFormDto) {
    		// SignUPFormDto
    		// User
    		int result = userRepository.insert(signUpFormDto);
    		if (result != 1) {
    			throw new CustomRestfullException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
    		}
    	}
    }

     

     

    회원가입(트랜잭션처리)

     

    1.dto 설계 하기 및 개념 확인
    2. UserController 설계 및 유효성 검사
    3. UserService 설계와 DI 사용
    4. SERVICE LAYER에 이해
    5. 트렌젝션에 이해

     

    DTO 패키지와 Model 패키지를 분리하는 것이 바람직하다.

     

     DTO(Data Transfer Object)와 모델 클래스를 분리하여 패키지를 만드는 것이 좋습니다. 그 이유는 다음과 같습니다:

    1. 코드의 가독성 및 유지 보수성:
    DTO와 모델 클래스를 별도의 패키지로 구분함으로써 코드의 구조가 명확해지고, 관련 클래스를 찾기 쉬워집니다. 이를 통해 유지 보수성이 향상됩니다.

    2. 객체의 역할 구분: 모델 클래스는 데이터베이스의 테이블 구조를 표현하는 반면, DTO는 클라이언트와 서버 간의 데이터 전송을 담당합니다. 이 두 객체의 역할이 다르기 때문에, 별도의 패키지로 구분하는 것이 좋습니다.

    3. 유연한 변경: 애플리케이션의 요구 사항이 변경되면 DTO와 모델 클래스의 변경이 독립적으로 이루어질 수 있습니다. 이렇게 구조를 분리해 놓으면, 한쪽의 변경이 다른 쪽에 영향을 미치는 것을 최소화할 수 있습니다.

    따라서, 코드의 가독성, 유지 보수성 및 유연성을 높이기 위해 DTO 패키지와 모델 패키지를 따로 구성하는 것이 좋습니다.

     

    오류 페이지 클래스를 만들어줘서 username,password,fullname이 값이 안들어갔을때 창이 뜨도록 해준다.

    package com.tenco.bank.handler.exception;
    
    import org.springframework.http.HttpStatus;
    
    import lombok.Getter;
    
    // Ioc 대상이 아님 (필요할 때 직접 new 처리)
    @Getter
    public class CustomRestfullException extends RuntimeException {
    	
    	private HttpStatus status;
    	// throw new CustomReslfullException('페이지 못 찾음', 404);
    	public CustomRestfullException(String message,HttpStatus status) {
    		super(message);
    		this.status = status;
    	}
    	
    }

     

     

    @PostMapping("/sign-up")
    	public String signUpProc(SignUpFormDto signUpFormDto) {
    		
    		// 1. 유효성 검사
    		if (signUpFormDto.getUsername() == null || signUpFormDto.getUsername().isEmpty()) {
    			throw new CustomRestfullException("username을 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    		if(signUpFormDto.getPassword() == null || signUpFormDto.getPassword().isEmpty()) {
    			throw new CustomRestfullException("password를 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    		if (signUpFormDto.getFullname() == null || signUpFormDto.getFullname().isEmpty()) {
    			throw new CustomRestfullException("fullname을 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    	
    	
    		
    		return "redirect:/user/sign-in";
    		
    	}

     

     

     

     

     

    package com.tenco.bank.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.tenco.bank.dto.SignUpFormDto;
    import com.tenco.bank.handler.exception.CustomRestfullException;
    import com.tenco.bank.repository.interfaces.UserRepository;
    
    @Service	//Ioc 대상 // 싱글톤 패턴으로 관리
    public class UserService {
    
    	@Autowired // DI 처리 (객체 생성시 의존 주입 처리)
    	private UserRepository userRepository;
    	
    	@Transactional 
    	// 메서드 호출이 시작될 때 트랜잭션에 시작
    	// 메서드 종료시 트랜젝션 종료 (commit)
    	public void singUp(SignUpFormDto signUpFormDto) {
    		// SignUPFormDto
    		// User
    		int result = userRepository.insert(signUpFormDto);
    		if (result != 1) {
    			throw new CustomRestfullException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
    		}
    	}
    }

     

    유저 서비스를 만들고 기능을 하는 메서드를 만들어서 model을 담아 insert 기능을 실행 시키고

    만약 result값이 1이 아닐때 회원가입이 실패 됐다고 뜨게 만든다.

     

    	@PostMapping("/sign-up")
    	public String signUpProc(SignUpFormDto signUpFormDto) {
    		
    		// 1. 유효성 검사
    		if (signUpFormDto.getUsername() == null || signUpFormDto.getUsername().isEmpty()) {
    			throw new CustomRestfullException("username을 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    		if(signUpFormDto.getPassword() == null || signUpFormDto.getPassword().isEmpty()) {
    			throw new CustomRestfullException("password를 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    		if (signUpFormDto.getFullname() == null || signUpFormDto.getFullname().isEmpty()) {
    			throw new CustomRestfullException("fullname을 입력해주세요", HttpStatus.BAD_REQUEST);
    		}
    		userService.singUp(signUpFormDto);
    		
    		return "redirect:/user/sign-in";
    		
    	}

    컨트롤러에서 UserService를 맴버변수로 들고온후 insert를 들고와 실행을 시킨다.

     

     

    회원가입창에 값을 입력하고 db에 들어오는지 확인한다.

    http://localhost:8080/h2-console/

     

     

     

     

     

     

    서비스 레이어를 만드는 이유는 뭘까?

    코드 결합도를 낮추고 재 사용성을 높이기 위함

    Web Layer : Rest API를 제공하며, Client 중심의 로직 적용 Business Layer : 내부 정책에 따른 logic를 개발하며, 주로 핵심업무 부분을 개발 Data Layer : 데이터 베이스 및 외부와의 연동 처리
    서비스는 일반적으로 비즈니스 로직을 수행하는 클래스입니다.

    비즈니스 로직(Business Logic)이란, 애플리케이션에서 수행되는 비즈니스 규칙이나 프로세스 등을 의미합니다. 즉, 애플리케이션의 핵심적인 업무 처리를 담당하는 로직을 말합니다

    예를 들어, 온라인 쇼핑몰에서 주문을 처리하는 기능은 주문 상태를 변경하거나, 재고 수량을 업데이트하는 등의 처리 과정을 수행합니다. 이러한 과정들이 바로 비즈니스 로직에 해당됩니다. 또한, 비즈니스 로직은 애플리케이션에서 수행되는 모든 로직 중에서 가장 중요한 부분으로, 애플리케이션의 성격과 특성에 따라 다양한 형태로 구현될 수 있습니다.

    Spring Boot에서 비즈니스 로직은 주로 서비스(Service) 레이어에서 구현됩니다. 이를 통해 비즈니스 로직을 분리하여 컨트롤러(Controller)나 뷰(View)와 같은 다른 레이어와 분리하여 개발하고, 애플리케이션의 유지 보수성과 확장성을 높일 수 있고 트랜잭션 관리도 가능 합니다.


     

    트랜잭션에 이해

     트랜잭션(Transaction)은 데이터베이스(Database)에서 수행되는 작업의 단위를 의미합니다. 즉, 데이터베이스에서 데이터를 읽거나 쓰는 작업을 수행할 때, 한 번에 실행되어야 하는 일련의 작업을 의미합니다.

    트랜잭션은 ACID라는 성질을 갖습니다. ACID는 원자성(Atomicity), 일관성(Consistency), 고립성(Isolation), 지속성(Durability)의 약어입니다. 이 중에서 원자성은 트랜잭션이 성공하거나 실패할 때, 모든 작업이 반영되거나, 아무 것도 반영되지 않아야 한다는 것을 의미합니다. 즉, 트랜잭션에서 한 번에 처리되어야 하는 작업들은 모두 완전히 수행되거나, 아예 수행되지 않아야 합니다.

    예를 들어, 은행에서 계좌 이체를 처리하는 작업을 수행할 때, 계좌에서 출금하는 작업과 입금하는 작업은 원자성을 가져야 합니다. 즉, 출금 작업이 완전히 수행되지 않았을 경우, 입금 작업도 반영되지 않아야 합니다.

    Spring Boot에서는 트랜잭션을 처리하기 위해 @Transactional 어노테이션을 제공합니다. 이 어노테이션을 사용하면, 트랜잭션 범위 내에서 실행되는 모든 작업이 원자성을 갖도록 보장할 수 있습니다. 또한, 트랜잭션의 고립성, 일관성, 지속성 등의 ACID 성질을 보장하기 위해 다양한 설정 옵션을 제공합니다.

     

    트랜잭션 1차 연습

    START TRANSACTION;
    
    insert into user_tb(username, password, fullname)
    values ('john', '1234', 'kim');
    
    insert into user_tb(username, password, fullname)
    values ('Mike', '222', 'Lee');
    
    ROLLBACK;

     

    트랜잭션 2차 연습

    /*
    	계좌 간 이체 
        계좌 A의 잔액은 3,000원 입니다. 
        계좌 B의 잔액은 0원 입니다. 
        계좌 A에서 B로 3000원 이체 하기 
    */
    -- 테스트를 위한 설정 
    UPDATE account_tb SET balance = 3000 where id = 1; 
    UPDATE account_tb SET balance = 0 where id = 2; 
    
    select * from account_tb;
    select * from history_tb; 
    
    START TRANSACTION;
    UPDATE account_tb set balance = balance - 3000 WHERE id = 1; 
    UPDATE account_tb set balance = balance + 3000 WHERE id = 2; 
    
    INSERT INTO history_tb(amount, w_balance, 
    	d_balance, w_account_id, d_account_id)
    VALUES(3000, 
    	(SELECT balance from account_tb where id = 1),
        (SELECT balance from account_tb where id = 2),
        1, 2);    
    
    COMMIT;

     

    첫번째 사용자가 두번 SELECT 할 당시 그 사이에

    두번째 사용자가 UPDATE를 했을때 트랜젝션을 사용하면

    데이터가 좀더 정확하게 나온다.

     

     

     

     

    'spring boot' 카테고리의 다른 글

    bank app 16 - 계좌 상세보기(1)  (0) 2023.04.20
    bank app 10  (0) 2023.04.18
    bankapp 8 - Exception Handler  (0) 2023.04.18
    bank app7 - 화면 구현(2)  (0) 2023.04.17
    bank app6 - 화면구현(1)  (0) 2023.04.17
Designed by Tistory.