[Spring Boot] 스프링부트 기초개념-Spring의 동작원리2
Git을 사용하면서 브랜치 전체 Clone하지 않고, 특정 브랜치 하나만 사용해야할 경우가 있다.
하지만, 기본적으로 알고 있는 방법으로 git clone할 경우 Master가 바로 Clone됨으로, 특정 브랜치만 클론하는 방법을 알아보자
1 | git clone -b <branch명> --single-branch <저장소URL> |
[Spring Boot] 데이터베이스 격리수준: Database Isolation Level
일이 처리되기 위한 가장 작은 단위
트랜잭션들이 모여 하나의 트랜잭션을 이룰 수 있고, 서비스가 된다.
→ 하나의 작업을 수행하기 위해 필요한 데이터베이스의 연산들을 모아놓은 것
T1: 트랜잭션 시작 → Update문 (테이블 내의 정보 수정 - 222.Busan -> 222.Jeju)
T2: T1이 Update하는 동안 그 부분을 Select를 하면 T2는 수정되기 전의 정보를 Select한다.
222.Busan (Undo영역 수정 전)
-------------------------
T1: Update한 것을 Commit (Undo영역이 수정 됨)
T2: T1이 commit하기 전에는 수정하기 전 정보를 Select하게 되고, Commit 이후에 Select하면 수정 된 정보를 볼 수 있다.
222.Jeju (Undo영역 수정 됨)
T2는 트랜잭션을 실행하지않고, Select만 한다.
Busan이 나오다가 갑자기 Jeju가 나와버렸다.
만약에, T2입장에서 T1과 비슷한 시기에 트랜잭션을 실행하고, Select를 계속 한다.
그리고 마지막에 그 Select한 결과들을 모아 Insert연산을 하고 Commit을 한다면...
중간에 데이터가 변경되어 결과가 예상과 달라져버리면 문제가 된다.
이런 문제는 금전적인 처리에서 주로 발생한다.
총 3번 각각 만원씩 Select를 할려고 했는데, 마지막 Select에서 2만원이 나오면,
예상은 3만원 Insert를 하려고 했는데, 4만원이 Insert - Commit이 되어버린다.
이를 해결하기 위해 REPEATABLE READ방식을 사용해야한다.
transaction Id부여
T2가 먼저 Transaction 시작(Id=10)
222를 Select하면 Busan
T1이 Transaction 시작 (Id=12)
222.Busan --> Jeju upate
commit
하지만 T2입장에서는 아직 자신의 Transaction이 종료되지 않았기 때문에, 항상 동일한 결과를 보여준다.
그래서 시작했을 때는 Busan이 나왔으므로, 끝까지 Busan이 나온다.
자신의 Transaction Id보다 작은 Undo로그를 보고 select한다.
C(Insert), U(Update), D(Delete) --> commit이 필요하므로
@Transactional 붙인다.
R(Select)는 보통 붙이지 않는데, 정합성을 위해 꼭 @Transactional을 붙여서
트랜잭션을 타게 해준다.
클라이언트 (브라우저)는 서버에 요청을 하고, 서버는 클라이언트에게 응답한다.
클라이언트가 서버에게 화면을 요청하면, 서버가 .html로 응답하여 브라우저는 그 html 파일을 읽는다.
메인화면에서 회원가입 수행을 요청하면, 서버는 DB에 회원가입을 수행하고 완료하면 다시 메인화면으로 돌아올 것이고, 결국 html로 응답해줘야하는데…
그 클라이언트가 꼭 브라우저인 것은 아니다. 앱일 경우, html를 반환해주면 이해하지 못한다.
그래서 Data만 반환해주고, 화면을 앱안에서 자체적으로 띄운다.
➡ 브라우저를 위한 서버, 앱을 위한 서버 두개를 따로 만들어야하지만, 차라리, Data를 반환해주는 서버 하나를 만들면 되지 않을까…
서버는 브라우저/앱에게 정상이라는 Data를 반환해준다.
브라우저는 다시 서버에서 request를 보내 html파일을 반환하게 한다.
앱은 자체적으로 화면이동을 한다.
그래서 Data를 Ajax를 사용한다.
보통 일을 수행할 때 1, 2, 3, 4, 5, … 순서대로 실행-종료,실행-종료… 절차적으로 수행한다.
그리고 1: 화면에 그림(내장), 2: 연산, 3: 그림 다운로드(외장), 4: 그림을 그림, 5: 화면에 그림(내장) 이라는 상황일 때, 1,2번이 수행되고, 3번 다운로드하는 10초 pending동안 앱은 멈춰있을 것이다.. 10초 다운로드가 끝나면 4,5번이 수행된다. 이 결과 UX가 나빠진다. 프로그램을 사용하고 싶지 않을 듯
비동기 통신은 절차적으로 일을 수행하는데, 일의 순서에 상관없이 수행한다.
1,2 수행하고, 3번을 비동기 처리하고 4번은 3번이 수행되어야 동작할 수 있기때문에, 5번을 수행하고 있는다.
3번 다운로드하는 동안 4번은 3번을 기다리고, 5번 그림 그리기 수행
➡ 비동기라고 한다.
git status나 commit 할 때, 한글이름을 가지는 파일일 경우에 \352\271\200\ 이런식으로 파일명이 깨지는 경우가 있다.
위 사진처럼 한글이 깨진다면, 아래 코드 한줄을 입력한다.
1 | git config --global core.quotepath false |
다시 확인해보면, 아래 사진과 같이 파일명이 잘 나오는 것을 볼 수 잇다.
[Spring Boot] JAVA 오버로딩과 오버라이딩
자바에서 다형성을 지원하는 방법으로 메서드를 오버로딩, 오버라이딩을 할 수 있다.
=> 오버로딩은 기존에 없던 새로운 메서드를 정의하는 것
=> 오버라이딩은 상속 받은 메서드의 내용만 변경하는 것
[Spring Boot] 데이터베이스 Update 하는 법
웹 브라우저에서 회원 수정을 하는 경우를 생각하며,
@PutMapping을 이용해서 주소를 만들어줬다. 이때 적은 주소는 @GetMapping의 주소와 동일한데, 스프링부트에서는 알아서 Get, Put을 구별해준다.
주소에서 id를 받아온다. 이 id는 데이터베이스에 저장 된 id값을 불러오기 위함이다.
그리고 @ReqeustBody를 이용하여 Json 데이터를 요청하여, 이를 JavaObject로 변환한다.
SpringBoot에서는 MessageConverter가 Jackson 라이브러리를 사용하여 자동적으로 변환해준다.
1 | // email, password |
Select 때와 마찬가지로, 잘못 된 (없는) id값을 받았을 때를 방지하기 위해 IllegalArgumentException을 throw 할 수 있도록 한다.
정상적인 user객체에 수정하고자 했던 데이터(password와 email)을 set으로 수정해준다.
그리고 save함수를 이용하여 update해준다.
id를 전달하지 않으면 insert를 해주고,
id를 전달하고, 해당 id에 대한 데이터가 있으면 update를 해준다.
id를 전달하지만, 해당 id에 대한 데이터가 없으면 insert를 해준다.
@Transactional라는 annotaion을 사용해보자.
1 | // email, password |
첫 번째 방법과 크게 다른 것은 없지만, @Transactional을 이용하면, Save함수를 사용하지 않아도 된다.
이를 Dirty Checking (더티체킹) 이라고 한다.
updateUser라는 함수가 실행될 때, Transaction이 실행되고, return이 될때 Transaction이 자동으로 종료되며, 자동 commit이 된다.
영속성 컨텍스트와 더티체킹에 대해서 공부해보자!
[Spring Boot] 데이터베이스 Select 하는 법과 에러체크
데이터베이스의 데이터를 Select할 때 잘못 된 인수가 들어가면 어떻게 해야할까
User table을 select하기에 앞서,
UserRepository라는 인터페이스 파일을 새로 만들고, 그 UserRepository는 JpaRepository를 상속하고 있다.
1 | // 자동으로 bean등록이 된다. --> @Repository 생략가능 |
그리고 select 기능을 넣어줄 클래스파일에 DI를 해주고,
User의 객체를 return 받을 수 있게, 메소드를 받들어주고, 주소를 넣어준다.
1 |
|
이 때, 만약 id부분에 DB에 없는 값이 들어가면 어떻게 해야할까… 라는 생각이 들 수 있다.
이 경우, user가 null이 되고, 결국 null값을 반환해주므로 프로그램에 문제가 생길 수 있다.
그래서 findById() 는 Optional로 User객체를 감싸서 반환해준다. 이후 우리가 null인지 아닌지 판단해서 사용하면 된다.
id값이 유효하면 그대로 user를 반환해주면 되지만, 유효하지 않을 경우 orElseGet을 타서 user에 User()빈 객체를 넣어줄 것이다. 이건 그냥 null과 다르다
orElseGet()에 넣을 수 있는 파라미터는 Supplier의 타입(인터페이스)이고, 이 타입의 Generic부분에 ?가 되어있는데 익명 객체를 넣어준다. 그리고 함수 get을 Override 해준다.
(인터페이스는 new할 수 없기 때문에, 익명 클래스를 만들어줘야한다. )
1 | User user = userRepository.findById(id).orElseGet(new Supplier<User>() { |
하지만 findByID()에서 만약 id가 null일 경우 IllegalArgumentException 를 throw하라고 적혀있다.
1 | User user = userRepository.findById(id).orElseThrow(new Supplier<IllegalArgumentException>() { |
1 | User user = userRepository.findById(id).orElseThrow(()->{ |
Spring에는 AOP라는 기능이 있다.
만약 Illegal이 있을경우, 에러를 가로채서 에러페이지로 넘어갈 수 있게 만들 수 있을 것이다.
요청: 웹브라우저
user 객체: 자바 오브젝트
@RestController: html파일이 아닌 data를 리턴해주는 controller
이 경우 웹브라우저가 이해할 수 있는 데이터 (Json)으로 변환해줘야하는데, SpringBoot는 MessageConter가 자바 오브젝트가 리턴될 시 자동으로 Jackson라이브러리를 호출해서 user 오브젝트를 Json으로 변환해서 브라우저에게 전달한다.