day40 🌕

장미·2022년 7월 23일
0

오늘의 성과

목록 보기
40/129

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 섹션 7 수강

+) 22. 07. 27. 추가

섹션 7. 스프링 MVC - 웹 페이지 만들기 수강 완료!!

타임리프 사용 선언
<html xmlns:th="http://www.thymeleaf.org">

속성 변경 - th:href
th:href ➡️ href="value1"을 th:href="value2"의 값으로 변경한다.
HTML을 그대로 볼 때는 href 속성이 사용되고, 뷰 템플릿을 거치면 th:href의 값이 href로 대체되면서 동적으로 변경할 수 있다.

리터럴 대체
리터럴 대체 문법을 사용하면 다음과 같이 편리하게 사용할 수 있다.
th:onclick="|location.href='@{/basic/items/add}'|"

속성 변경 - th:action
HTML form에서 action에 값이 없으면 현재 URL에 데이터를 전송한다.
상품 등록 폼의 URL과 실제 상품 등록을 처리하는 URL을 똑같이 맞추고 HTTP 메서드로 두 기능을 구분한다.

  • 상품 등록 폼: GET/basic/items/add
  • 상품 등록 처리: POST/basic/items/add

➡️ 하나의 URL로 등록 폼과 등록 처리를 깔끔하게 할 수 있다.


@ModelAttribute

  • 객체를 생성하고 요청 파라미터의 값을 프로퍼티 접근법(setXXX)으로 입력해준다.
  • 모델(Model)에 @ModelAttribute로 지정한 객체를 자동으로 넣어준다.
  • 모델에 데이터를 담을 때는 이름이 필요하다. 이름은 @ModelAttribute에 지정한 name(value) 속성을 사용한다.
  • @ModelAttribute의 이름을 생략할 수 있다. 이름을 생략하면 모델에 저장될 때 클래스명을 사용한다. 이때 클래스의 첫글자만 소문자로 변경해서 등록한다.
  • @ModelAttribute 자체도 생략 가능하다.

리다이렉트
상품 수정은 마지막에 뷰 템플릿을 호출하는 대신 상품 상세 화면으로 이동하도록 리다이렉트를 호출한다. ➡️ redirect:/...

PRG - Post/Redirect/Get
POST 등록 후 새로고침 하면 상품이 중복 등록된다. (POST /add가 또 실행된다.)

웹 브라우저의 새로고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.
그래서 내용은 같고, ID만 다른 상품 데이터가 계속 쌓이게 된다.

새로고침 문제를 해결하려면 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해주면 된다.
웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다. 따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id}가 되는 것이다.
이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로고침 문제를 해결할 수 있다.


섹션 8. 다음으로 수강 완료!!


토픽 - DB 트랜잭션

+) 22. 07. 31. 01:55 추가 👍 (스압 조심)

트랜잭션이란 상점에서 고객의 주문이나 판매, 은행에서 예금주의 입금이나 출금과 같은 하나의 외부 거래를 기록하기 위해 컴퓨터 시스템 내부에서 완료되어야 하는 일련의 처리 동작을 말한다.
트랜잭션에는 파일 내용의 갱신, 수신 응답의 통보 등이 포함된다.

- IT용어사전, 트랜잭션


트랜잭션의 개념

데이터베이스는 다수의 사용자가 동시에 사용하더라도 항상 모순이 없는 정확한 데이터를 유지해야 한다. 그리고 데이터베이스에 장애가 발생하더라도 빠른 시간 내에 원래의 상태로 복구할 수 있어야 한다.
DBMS은 데이터베이스가 항상 정확하고 일관된 상태를 유지할 수 있도록 다양한 기능을 제공하는데, 그 중심에는 트랜잭션이 있다.

트랜잭션은 하나의 작업을 수행하기 위해 필요한 DB의 연산들을 모아놓은 것으로, DB에서 논리적인 작업의 단위가 된다. 트랜잭션은 장애가 발생했을 때 데이터를 복구하는 작업의 단위도 된다.
일반적으로 DB 연산은 SQL문으로 표현되므로 트랜잭션을 작업 수행에 필요한 SQL문들의 모임으로 이해해도 좋다.

참고
트랜잭션이 작업 수행의 논리적 단위이기 때문에, DBMS의 성능은 초당 트랜잭션의 실행 수(TPS, Transaction Per Second)로 측정한다.

예를 통해 트랜잭션의 개념을 좀 더 명확히 이해해보자.

계좌 이체 트랜잭션은 두 개의 UPDATE 문으로 구성되어 있다. 두 UPDATE 문을 처리하는 순서는 중요하지 않지만 둘 다 정상적으로 실행되어야 한다.

만약 첫 번째 UPDATE 문이 실행된 후, 시스템에 장애가 발생하여 두 번째 UPDATE 문이 실행되지 않으면 장미가 5,000원을 이체했으나 은우가 이를 받지 못해 5,000원이 사라지는 모순된 상황이 발생한다.

이러한 모순된 상황이 발생하지 않도록 하려면 시스템이 정상적으로 작동하게 되었을 때 두 번째 UPDATE 문을 실행하여 트랜잭션의 모든 UPDATE 문이 정상적으로 실행되도록 하거나, 첫 번째 UPDATE 문의 실행을 취소하여 데이터베이스를 트랜잭션 작업 전의 상태로 되돌아가도록 해야 한다.


트랜잭션의 특징

  • 원자성(Atomicity)
    트랜잭션의 연산은 데이터베이스에 모두 반영되거나 아니면 전혀 반영되지 않아야 한다.
    트랜잭션 내의 모든 명령은 반드시 완벽히 수행되어야 하며, 모두가 완벽히 수행되지 않고 어느 하나라도 에러가 발생하면 트랜잭션 전부가 취소되어야 한다.

  • 일관성(Consistency)
    트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 변환한다.
    시스템이 가지고 있는 고정요소는 트랜잭션 수행 전과 트랜잭션 수행 완료 후의 상태가 같아야 한다.

    • 예: 아래 이미지처럼, 계좌 이체 전의 잔액 합계가 10,000원일 때, 트랜잭션 수행 후에도 계좌 잔액의 합계가 똑같이 10,000원이어야 한다.

      만약 트랜잭션이 수행된 후에 장미의 계좌 잔액은 5,000원으로 감소했지만, 은우의 계좌 잔액은 0원으로 잔액의 합계가 5,000원이 되었다고 해보자.
      이 경우 DB는 5,000원이 사라진 모순된 상태가 되어 일관성을 만족하지 않으므로 트랜잭션의 수행이 성공적으로 완료됐다고 볼 수 없다.

  • 독립성(Isolation)
    둘 이상의 트랜잭션이 동시에 병행 실행되는 경우 어느 하나의 트랜잭션 실행 중 다른 트랜잭션의 연산이 끼어들 수 없으며, 수행 중인 트랜잭션은 완전히 완료될 때까지 다른 트랜잭션에서 수행결과를 참조할 수 없다.
    일반적으로 데이터베이스 시스템에서는 여러 트랜잭션이 동시에 수행되지만, 각 트랜잭션이 독립적으로 수행될 수 있도록 다른 트랜잭션의 중간 연산 결과에 서로 접근하지 못하게 한다.

    참고
    독립성의 보장은 OS의 세마포어와 비슷한 개념으로, Lock을 통해 보장할 수 있다.
    즉, 데이터를 읽거나 쓸 때는 문을 잠가서 다른 트랜잭션이 접근하지 못하도록 하고, 수행을 마치면 Unlock을 통해 데이터를 다른 트랜잭션이 접근할 수 있도록 허용한다.

  • 영속성(Durability)
    성공적으로 완료된 트랜잭션의 결과는 영구적으로 반영되어야 한다.
    시스템에 장애가 발생하더라도 트랜잭션 작업 결과는 없어지지 않고 데이터베이스에 그대로 남아있어야 한다.


트랜잭션의 연산

  • Commit
    트랜잭션의 수행이 성공적으로 완료되었음을 선언하는 연산이다. commit 연산이 실행된 후에야 트랜잭션의 수행 결과가 데이터베이스에 반영되어, 데이터베이스가 일관된 상태를 지속적으로 유지하게 된다.

    BEGIN TRANSACTION;
       
     UPDATE accounts
     SET balance = balance - 1000
     WHERE account_no = 100;
       
     UPDATE accounts
     SET balance = balance + 1000
     WHERE account_no = 200;
        
     INSERT INTO account_changes(account_no,flag,amount,changed_at) 
     VALUES(100,'-',1000,datetime('now'));
       
     INSERT INTO account_changes(account_no,flag,amount,changed_at) 
     VALUES(200,'+',1000,datetime('now'));
       
     COMMIT;
    트랜잭션 커밋 사용 예시

    BEGIN TRANSACTION과 COMMIT 사이에 있는 UPDATE, INSERT문은 전체 다 실행되거나 실행되지 않는다. (→ 원자성)

    만약 account_no 100에 잔액이 20,000원, account_no 200에 잔액이 10,000원 있다면 위 코드 실행 시 잔액은 아래와 같이 바뀔 것이다.

    그리고 account_changes를 조회해보면 각 입출금 기록이 아래와 같이 남아있는 것을 볼 수 있다.

  • Rollback
    트랜잭션의 수행이 실패했음을 선언하는 연산이다. 롤백 연산이 실행되면 트랜잭션이 지금까지 실행한 연산의 결과가 취소되고, 트랜잭션이 수행되기 전의 상태로 다시 돌아간다.
    트랜잭션이 수행되는 도중 장애가 발생하여 일부 연산이 처리되지 못한 상황에서는 롤백 연산을 실행하여 트랜잭션의 수행이 실패했음을 선언하고, 모순되지 않도록 DB를 트랜잭션 수행 전의 일관된 상태로 되돌려야 한다.

    BEGIN TRANSACTION;
       
     INSERT INTO account_changes(account_no,flag,amount,changed_at) 
     VALUES(100,'-',20000,datetime('now'));
       
     UPDATE accounts
     SET balance = balance - 20000
     WHERE account_no = 100;
    트랜잭션 롤백 사용 예시

    위 코드를 실행할 경우 Uncaught Error: CHECK constraint failed: accounts 라는 에러가 발생한다.
    왜냐하면 잔액이 0원 이상이어야 하기 때문이다. 아까 account_no 100의 계좌에는 총 19,000원이 있었는데, 20,000원을 송금하려 하니 오류가 난 것이다.

    CREATE TABLE accounts ( 
    		account_no INTEGER NOT NULL,
    		balance DECIMAL NOT NULL DEFAULT 0,
    		PRIMARY KEY(account_no),
    		CHECK(balance >= 0)
       );
       
     CREATE TABLE account_changes (
    		change_no INTEGER PRIMARY KEY AUTOINCREMENT,
    		account_no INTEGER NOT NULL, 
    		flag TEXT NOT NULL, 
    		amount DECIMAL NOT NULL, 
    		changed_at TEXT NOT NULL 
       );
    테이블 생성 코드 (CHECK 부분 참고)

    이 때 account_changes를 조회하면 아래와 같은 결과가 나온다. (아직 트랜잭션을 종료하지 않은 상태다. → 커밋이나 롤백하지 않음.)

    accounts를 조회하면 아래와 같이 나온다.

    즉, 아직 돈이 안 빠져나갔다는 소리다. 하지만 돈이 빠져나간 기록은 남아있다. 바로 이런 경우 문제가 생기는 것이고, 롤백을 사용해야 한다.

    ROLLBACK;

    롤백 실행 후 account_changes를 다시 조회해 보면 다음과 같은 결과가 나온다.

커밋, 롤백 관련 부분 코드는 글로만 보면 조금 이해가 안 될 수도 있다. 아래 참고 자료에 있는 박재호 님의 “트랜잭션이 뭐지?”를 보면 좀 더 이해가 잘 될 것이다.

참고: 만약 트랜잭션의 길이가 길어지면 어떻게 롤백해야 할까?
확실하게 오류가 발생하지 않는 부분도 다시 처음부터 작업을 수행해야 한다. 따라서 확실한 부분에 대해서는 롤백이 되지 않도록 중간 저장 지점인 save point를 지정할 수 있다.
save point를 지정하면 롤백할 때, save point 이전은 확실하다 간주하고 그 이후부터 롤백을 진행하게 된다.


트랜잭션의 상태

트랜잭션의 다섯 가지 상태
  • 활동 상태
    트랜잭션이 수행을 시작하여 현재 수행 중인 상태. 상황에 따라 부분 완료 상태나 실패 상태가 된다.

  • 부분 완료 상태
    트랜잭션의 마지막 연산이 실행된 직후의 상태. 트랜잭션의 모든 연산을 처리한 상태지만, 트랜잭션이 수행한 최종 결과를 DB에 아직 반영하지 않은 상태이므로 아직은 트랜잭션의 수행이 성공적으로 완료됐다고 볼 수 없다.
    상황에 따라 완료 상태나 실패 상태가 될 수 있다.

  • 완료 상태
    트랜잭션이 성공적으로 완료되어 commit 연산을 실행한 상태. 트랜잭션이 수행한 최종 결과를 DB에 반영하고, DB가 새로운 일관된 상태가 되면서 트랜잭션이 종료된다.

  • 실패 상태
    하드웨어, 소프트웨어의 문제나 트랜잭션 내부의 오류 등의 이유로 장애가 발생하여 트랜잭션의 수행이 중단된 상태.
    트랜잭션이 더는 정상적으로 수행을 계속할 수 없을 때 실패 상태가 된다.

  • 철회 상태
    트랜잭션의 수행이 실패하여 rollback 연산을 실행한 상태. 철회 상태가 되면 지금까지 실행한 트랜잭션의 연산을 모두 취소하고 트랜잭션이 수행되기 전의 DB 상태로 되돌리면서 트랜잭션이 종료된다.
    트랜잭션의 내부 문제가 아닌, 하드웨어 이상이나 소프트웨어의 오류로 트랜잭션의 수행이 중단되고 철회 상태가 된 경우에는 철회된 트랜잭션을 다시 시작한다.
    하지만 트랜잭션이 처리하려는 데이터가 DB에 존재하지 않거나 트랜잭션의 논리적인 오류가 원인인 경우에는 철회된 트랜잭션을 폐기한다.


트랜잭션의 격리 수준(Isolation Level)

동시에 여러 트랜잭션이 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지 결정하는 것을 말한다.
→ 동시에 DB에 접근할 때, 그 접근을 어떻게 제어할지에 대한 결정.

  • READ UNCOMMITTED
    커밋 전의 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 읽을 수 있도록 허용한다.

    트랜잭션 A가 아직 커밋되지 않은 상태에서 트랜잭션 B가 DB를 조회하면, 변경된 트랜잭션 A의 값을 볼 수 있게 된다.
    만약 트랜잭션 A가 일을 다 수행하지 못하고 롤백한다면 트랜잭션 B는 무효가 된 데이터 값을 읽고 처리하기 때문에 문제가 생긴다. 이것이 바로 Dirty Read 현상이다.

  • READ COMMITED
    커밋이 완료된 트랜잭션의 변경사항만 다른 트랜잭션에서 조회가 가능하다. 트랜잭션이 이루어지는 동안 다른 트랜잭션은 해당 데이터에 접근이 불가능하다. (→ A가 커밋하고 나서야 그 때 A가 수정한 내용을 읽어올 수 있다.)

    아직 커밋하기 전이라면 트랜잭션 시작 전의 값을 읽어오고, 커밋하면 변경된 데이터를 읽어온다.
    같은 트랜잭션 내(트랜잭션 B)에서 SELECT 문을 두 번 조회했는데, 그 결과가 각각 다르다. 즉, 데이터 불일치 문제가 생긴다. 이것을 Non-Repeatable Read라고 한다.

  • REPEATABLE READ
    트랜잭션 범위 내에서 조회한 내용이 항상 동일함을 보장한다.
    READ COMMITED와 마찬가지로 커밋이 완료된 데이터만 읽을 수 있다. 다른 점은, 한 트랜잭션이 조회한 데이터는 그 트랜잭션이 종료될 때까지 다른 트랜잭션이 변경하거나 삭제하는 것을 막는다.

    한 번 조회한 데이터는 반복적으로 조회해도 같은 값을 반환한다.
    하지만 REPEATABLE READ는 Phantom Read라는 문제가 발생한다.

    Phantom Read는 Non-Repeatable Read의 한 종류로, 조건이 있든 없든 SELECT 문을 쓸 때 나타날 수 있다.
    다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다가 안 보였다가 하는 현상이다.
    → 해당 쿼리로 읽히는 데이터에 들어가는 행위. 데이터가 새로 생기거나 없어져 있는 현상을 말한다.
    이러한 문제를 해결하기 위해 Lock을 걸어야 한다.

  • SERIALIZABLE
    한 트랜잭션에서 사용하는 데이터는 다른 트랜잭션에서 접근할 수 없다.
    트랜잭션의 ACID 성질이 엄격하게 지켜지나 성능은 가장 떨어진다.
    단순한 SELECT 문만으로도 트랜잭션이 커밋될 때까지 모든 데이터에 잠금(Lock)이 설정되어 다른 트랜잭션에서 해당 데이터를 변경할 수 없게 된다.


참고 자료

  1. “Transaction”. NAVER 지식백과. 2022년 7월 30일 접속, https://terms.naver.com/entry.naver?docId=3483740&cid=58439&categoryId=58439

  2. 김연희, 데이터베이스 개론, https://terms.naver.com/entry.naver?docId=3431258&cid=58430&categoryId=58430&expCategoryId=58430

  3. 박재호, “[즐겁게 배우는 SQL #30] 트랜잭션이 뭐지?”, https://youtu.be/0DnuiH_nnoQ

  1. 우아한Tech, “[10분 테코톡] 🌼 예지니어스의 트랜잭션”, https://youtu.be/e9PC0sroCzc

함께 보면 좋은 자료

  1. “트랜잭션의 격리 수준(isolation Level)이란?”. Nesoy Blog. 2022년 7월 31일 접속, https://nesoy.github.io/articles/2019-05/Database-Transaction-isolation

  2. 우아한Tech, “[10분 테코톡] 🙊 에이든의 트랜잭션 메커니즘”, https://youtu.be/ImvYNlF_saE


베이스볼 수정 🔮


코테 1문제

profile
김뉴비

0개의 댓글

관련 채용 정보