Til (60)

 

 

 

 

 

 

단위 테스트(Unit Test)

 

 

➤ 단위 테스트(Unit Test) 란?

 

컴퓨터에서 단위 테스트는 소스 코드의 특정 모듈이 의도된 대로 잘 작동하는지 검증하기 위해 모든 함수와 메서드에 대한 테스트 케이스를 작성하는 절차를 말한다.

 

애플리케이션에서 Postman 같은 툴을 사용해서 테스트하면 정확하게 어느 부분에서 문제가 발생하는지 알기 어렵다.

Java에서는 메서드 같이 아주 작은 단위를 가지는 기능들을 테스트할 수 있다.

Spring도 역시 계층별로 테스트할 수 있는 테스트 기법을 지원해준다.

 

일반적으로 단위 테스트는 최대한 독립적이고 작은 단위인 것이 좋다.

 

참고) 유닛테스트 위키

 

 

 

▶️ 기능 테스트

 

  • 테스트 단위가 가장 큰 테스트
  • 애플리케이션을 사용하는 사용자 입장에서 애플리케이션이 제공하는 기능이 올바르게 동작하는지 테스트
  • 주로 테스트하는 주체는 개발자가 될수도 있지만 일반적으로 테스트 전문 부서(QA) 또는 외부 QA 업체가 되기도 한다.
  • API 툴이나 데이터베이스까지 연관되어 있어서 개발한 애플리케이션과 연관된 대상이 많기 때문에 단위 테스트라고 볼 수 없다.

 

 

▶️ 통합 테스트

 

  • 클라이언트 측 툴 없이 개발자가 작성한 테스트 코드를 실행시켜 이루어지는 경우가 많다.
  • 주로 개발자가 테스트의 주체가 된다.
  • 여러 계층이 연관되어 있고 DB까지 연결되어 있어서 독립적인 테스트가 가능하다고 볼 수 없으므로 단위 테스트라고 볼 수 없다.

 

 

▶️ 슬라이스 테스트

 

  • 애플리케이션을 특정 계층으로 쪼개어서 하는 테스트를 의미한다.
  • API 계층, 서비스 계층, 데이터 액세스 계층이 각각 슬라이스 테스트의 대상이 될 수 있다.
  • HTTP 요청이 필요하고, 외부 서비스가 연동되거나 데이터 액세스 계층은 DB와 연동되어 있기 때문에 단위 테스트라고 보기 어렵다.
  • Mock(가짜) 객체를 사용하여 계층별로 끊어서 테스트 할 수 있기 때문에 어느 정도 테스트 범위를 좁힐 수 있다.
  • 애플리케이션의 일부만 테스트하기 때문에 부분 통합 테스트라고 부르기도 한다.

 

 

👉🏻 DB를 사용하면 왜 단위 테스트라고 보기 힘든것일까?

통합 테스트나 슬라이스 테스트에서 DB와 연동된다고 해서 무조건 단위 테스트라고 부르기 어려운 것이 아니다. 데이터베이스의 상태가 테스트 전후로 동일하게 유지될 수 있다면 데이터베이스와 연동된다해도 단위 테스트에 포함될수는 있다.

 

 

 

▶️ 단위 테스트

 

  • 핵심 로직(비즈니스 로직)에서 사용하는 클래스들이 독립적으로 테스트하기 가장 좋은 대상이기 때문에 단위 테스트라고 부른다.
  • 의도한 대로 잘 동작하는지 확인하기 위해서는 메서드를 테스트하는 것이 좋다.
  • 단위 테스트 코드는 대부분 메서드 단위로 작성된다.

 

 

 

➤ 단위 테스트를 해야하는 이유?

 

  • 구현한 코드가 의도한대로 동작하는지 결과를 빠르게 확인할 수 있다.
  • 문제점을 쉽게 찾을 수 있다.
  • 언제라도 단위 테스트를 믿고 리팩토링을 할 수 있다.
  • 버그 리포트를 전달 받을 경우 버그가 발생한 기능의 테스트 케이스를 돌려보면서 문제가 발생한 원인을 단계적으로 찾아가기가 용이하다.

 

 

 

※ 테스트 케이스(Test Case) 란?

 

테스트를 위한 입력 데이터, 실행 조건, 기대 결과를 표현하기 위한 명세를 의미한다.

메서드 등 하나의 단위를 테스트하기 위해 작성하는 테스트 코드이다.

 

 

 

➤ F.I.R.S.T 원칙

 

단위 테스트를 위한 테스트 케이스를 작성하기 위한 가이드 원칙으로 F.I.R.S.T 원칙을 참고할 수 있다.

 

 

▶️ Fast(빠르게)

 

일반적으로 테스트 케이스는 빨라야 한다.

자주 돌려야 문제를 빨리 찾는데 너무 느려서 돌리기 힘들면 테스트 케이스를 작성하는 의미가 퇴색될 것이다.

 

 

▶️ Independent(독립적으로)

 

각각의 테스트 케이스는 독립적으로 동작해야 한다.

어떤 테스트 케이스를 먼저 실행해도 실행되는 순서와 상관없이 정상적인 실행이 보장되어야 한다.

 

 

▶️ Repeatable(반복 가능하도록)

 

어떤 환경에서도 반복해서 실행이 가능해야 한다.

외부 리소스나 외부 서비스와 연동하는 경우 동일한 테스트 결과를 보장하지 못하기 때문에 단위 테스트 시에는 외부와의 연동을 끊어주는 것이 바람직하다.

 

 

▶️ Self-validating(셀프 검증이 되도록)

 

성공 또는 실패라는 자체 검증 결과를 보여주어야 한다.

테스트 케이스 스스로가 결과가 옳은지 그른지 판단할 수 있어야 한다.

 

 

▶️ Timely(시기 적절하게)

 

테스트 하려는 기능 구현을 하기 직전에 작성해야 한다.

TDD(테스트 주도 개발) 방식에서는 기능 구현 전에 실패하는 테스트 케이스를 먼저 작성한다.

 

 

 

 

➤ JUnit 없이 비즈니스 로직에 단위 테스트 적용해보기

 

 

JUnit : Java 기반의 소프트웨어를 테스트하기 위한 표준 테스트 프레임워크

 

단위 테스트를 제일 쉽고 빠르게 적용할 수 있는 부분은 헬퍼(helper) 클래스 또는 유틸리티(utility) 클래스 이다.

 

 

 

▶️ Given-When-Then 표현 스타일

 

given-when-then 은 BDD(Behavior Driven Development)에서 사용하는 용어이다.

 

Given

  • 테스트를 위한 준비 과정을 명시한다.
  • 테스트에 필요한 전제 조건들이 포함된다.
  • 테스트 대상에 전달되는 입력 값(테스트 데이터)도 Given에 포함된다.

 

When

  • 테스트 할 동작(대상)을 지정한다.
  • 단위 테스트에선 일반적으로 메서드 호출 통해 테스트를 진행한다.

 

Then

  • 테스트의 결과를 검증하는 영역이다.
  • 예상하는 값과 테스트 대상 메서드의 동작 수행 결과 값을 비교해서 기대한대로 동작을 수행하는지 검증(Assertion)하는 코드가 포함된다.

 

 

✅ Assertion(어써션) 이란?

 

직역하면 단언, 단정 이라는 의미이다.

테스트에서는 어써션이라는 용어는 테스트 결과를 검증할 때 주로 사용한다.

예상하는 결과 값이 참이길 바라는 것이다.

단언문, 단정문이라고 표현하기도 한다.

조건문 등으로 직접 검증할 수 있다.

 

 

 

 

JUnit

 

 

➤ JUnit 이란?

 

Java 언어로 만들어진 애플리케이션을 테스트 하기 위한 Open source Test Framework로

Java의 표준 테스트 프레임워크라고 볼 수 있다.

TestNG라는 경쟁사도 있다.

Spring Boot의 디폴트 테스트 프레임워크는 JUnit이다.

 

 

추가학습) TestNG 란?

  • Cédric Beust가 2004년에 만든 자바 기반 testing framework 이다.
  • JUnit 과 NUnit 에서 영감을 받았다.
  • JUnit 과 오랫동안 경쟁한 테스팅 프레임워크이다.
  • NG는 Next Generation(차세대)라는 의미이다.
  • JUnit 과 다른 TestNG 만의 장점
    • group : Groups 주석으로 사용, 한번 컴파일 한 후엔 같은 그룹끼리 테스트가 가능하다.
    • multithread testing을 지원한다.

 

 

 

➤ JUnit 기본 작성법

 

Spring Boot Initializr 에서 Gradle 기반의 Spring Boot 프로젝트를 생성하고 오픈하면 기본적으로 test 디렉토리가 만들어진다.

그리고 기본적으로 스타터가 포함되고 JUnit도 포함되어 있다.

 

 

▶️ JUnit을 사용한 테스트 케이스의 기본 구조

 

애플리케이션에서 테스트 하고자 하는 대상(Target)이 존재하고

테스트 메서드 위에 @Test 애너테이션을 추가해준다.

 

 

 

▶️ Assertion 메서드 사용하기

 

✔️ assertEquals()

 

org.junit.jupiter.api.Assertions.assertEquals 를 import 해서 assertEquals 메서드를 사용할 수 있다.

 

 

 

✔️ assertNotNull()

 

  • Null 여부 테스트를 진행한다.
  • 첫 번째 파라미터 : 테스트 대상 객체
  • 두 번째 파라미터 : 테스트에 실패했을 때 표시할 메시지(생략 가능)

 

 

✔️ assertThrows()

 

  • 예외(Exception) 테스트
  • 원하는 예외를 던졌을 경우 테스트 passed
  • 결과 예외보다 상위 타입의 예외를 기대해도 동일하게 passed
  • 첫 번째 파라미터 : 발생이 기대되는 예외 클래스 (Exception.class)
  • 두 번째 파라미터 : 람다 표현식으로 테스트 대상 메서드를 호출 (Executable 타입)

 

 

※ Executable 함수형 인터페이스

assertThrows() 의 두 번째 파라미터인 람다 표현식은 JUnit에서 지원하는 Executable 함수형 인터페이스이다.

메서드 하나만 정의되어 있고 리턴값이 없다.

void execute() throws Throwable;

잠재적으로 Throwable 을 던질 수 있는 모든 일반 코드 블럭을 구현하는데 사용된다.

 

참고) Executable 공식문서

 

 

✔️ assertDoesNotThrow()

 

어떤 예외도 던지지 않는지 테스트 한다.

첫 번째 파라미터 : 람다 표현식으로 테스트 대상 메서드를 호출 (Executable 타입)

 

 

 

참고) JUnit5 Assertions 공식문서

 

 

 

▶️ 테스트 케이스 실행 전, 후처리

 

✔️ @BeforeEach

 

이 애너테이션을 추가한 메서드는 테스트 케이스가 각각 실행될 때 마다 케이스 실행 직전에 먼저 실행되어 초기화 작업 등을 진행할 수 있다.

 

 

✔️ @BeforeAll

 

클래스 레벨에서 테스트 케이스를 한꺼번에 실행 시키면 테스트 케이스가 실행되기 전에 한번만 초기화 작업을 한다.

⭐️ @BeforeAll() 에너테이션을 추가한 메서드는 정적 메서드(static method)여야 한다

 

 

 

▶️ 테스트 케이스 실행 후, 후처리

 

@AfterEach, @AfterAll 

@BeforEach, @BeforeAll() 과 동일하게 동작하는데 테스트 케이스 실행이 끝난 시점에 후처리 작업을 할 때 사용한다.

 

 

▶️ Assumption

 

Junit 5에 추가된 기능이다.

Assumption은 말 그대로 ‘가정’할 때 사용한다.

특정 환경에서만 테스트 케이스가 실행되도록 할 수있다.

 

org.junit.jupiter.api.Assumptions 안에서 메서드를 Import 해서 사용한다.

 

 

✔️ assumeTrue()

 

  • 파라미터로 입력된 값이 true 일 때 나머지 아래 로직을 실행한다.
  • 특정 OS 환경 등의 특정 조건에서 선택적인 테스트가 필요할 때 유용하다.

 

※ 실행환경의 OS name 검사하기

System.getProperty(“os.name”).startsWith(“Mac”)

assumeTrue(System.getProperty("os.name").startsWith("Mac"));

getProperty의 파라미터로 os.name을 String 타입으로 전달하면 실행환경의 OS 이름을 리턴한다.

startsWith는 해당 String이 파라미터로 전달하는 String으로 시작하는지 검사한다.

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😉

 

 

 

 

 

 

Session

 

 

➤ Session 이란?

 

  • 서버와 클라이언트 간 연결이 활성화된 상태에서 데이터를 서버에 저장
  • 서버가 클라이언트에 유일하고 암호화된 ID를 부여
  • 중요 데이터는 서버에서 관리

 

 

 

➤ Authorization (인가, 접근권한)

 

  • 사용자가 아이디와 비밀번호를 올바르게 입력해서 인증(Authentication)에 성공 ➡️ 서버가 해당 유저가 인증에 성공했다는 것을 저장
  • 인증에 따라 리소스 접근권한(Authorization)이 달라진다.
  • ex) 인증 후 user인지 admin인지에 따라 리소스의 접근권한이 달라진다.

 

 

 

➤ Session 기반 인증

 

 

session 기반 인증 흐름

 

 

사용자가 인증에 성공한 상태Session(세션) 이라고 부른다.

서버는 일종의 저장소에 세션을 저장한다.

주로 in-memory, 또는 세션 스토어(redis 등과 같은 트랜잭션이 빠른 DB)에 저장한다.

세션이 만들어지면 각 세션을 구분할 수 있는 세션 아이디도 만들어 진다. ➡️ 클라이언트에 세션 성공을 증명할 수단으로써 쿠키에 세션 아이디를 함께 전달

웹사이트에서 로그인을 유지하기 위한 수단으로 쿠키에 서버에서 발급한 세션 아이디를 저장한다.

 

 

 

➤ 로그아웃

 

  • 서버 : 세션 정보를 삭제
  • 클라이언트 : 쿠키를 갱신

 

서버가 임의로 클라이언트의 쿠키를 삭제할 수는 없다.

➡️ 대신 set-cookie로 전송할 때 세션 아이디의 키값을 무효한 값으로 갱신할 수 있다.

 

 

 

 

웹 보안 공격

 

 

➤ SQL Injection

 

  • 웹 해킹을 접한다면 가장 먼저 배우는 공격 기법
  • 간단하지만 아주 강력한 공격이다.
  • 데이터베이스에 임의의 SQL문을 실행할 수 있도록 명령어를 삽입하는 공격 유형이다.
  • 응용프로그램의 보안상의 허점을 이용해 데이터베이스를 비정상적으로 조작, 기록이 삭제되거나 유출될 수 있다.

 

SQL Injection 예시

 

 

▶️ SQL Injection 대응 방안

 

1. 입력(요청) 값 검증

SQL문은 자연어와 비슷하기 때문에 키워드를 막는데엔 한계가 있다.

➡️ 화이트리스트 방식으로 해당 키워드가 들어오면 다른 값으로 치환하여 SQL Injection을 막는다.

 

화이트리스트 : 보안에서 기본 정책이 모두 차단된 상황에서 예외적으로 접근이 가능한 대상을 지정하는 방식, 또는 그 지정된 대상 (반대말은 블랙리스트)

 

 

2. Prepared Statement 구문 사용

사용자의 입력값이 전달되기 전에 데이터베이스가 미리 컴파일하여 SQL을 바로 실행하지 않고 대기하며, 사용자의 입력값을 단순 텍스트로 인식한다. 입력값이 SQL문이 아닌 단순 텍스트로 적용되어 공격에 실패하게 된다.

 

 

3. Error Message 노출 금지

공격자는 데이터베이스의 Error Message를 통해 테이블이나 칼럼 등 데이터베이스의 정보를 얻을 수 있다. 에러가 발생한 SQL문과 에러 내용이 클라이언트에 노출되지 않도록 별도의 에러핸들링이 필요하다.

 

 

 

➤ Cross-Site Request Forgery (CSRF)

 

▶️ Web application Security란?

 

- 개발자들이 웹사이트, 모바일, 어플, 웹 API 등을 만들 때에 해커들의 공격을 막기 위해서 보안(security)은 필수 사항

- 여러가지 공격들

  • SQL Injection
  • XSS
  • CSRF

 

 

▶️ CSRF 란?

 

- 다른 사이트(cross-site)에서 유저가 보내는 요청(request)을 조작(forgery)하는 것

ex) 전자 게시판에 공지글로 위장하여 악성 코드를 심은 글을 작성한다. 사용자가 글을 클릭하면 은행계좌에서 돈이 빠져나감

 

- 해커가 직접 데이터를 접근할 수 없다.

ex) 다른 사이트이기 때문에 response에 직접 접근할 수 없음

 

 

▶️ CSRF 공격을 하기 위한 조건

 

- 쿠키를 사용한 로그인

유저가 로그인 했을 때, 쿠키로 어떤 유저인지 알 수 있어야 함

 

- 예측할 수 있는 request parameter를 가지고 있어야 함

request에 해커가 모를 수 있는 정보가 담겨있으면 안됨

 

 

▶️ GET 요청으로 CSRF 공격하기

 

계좌이체에 사용되는 GET 요청

http://wisebank.com/transfer?account_number=useraccount&amount=100000$

 

 

사용자의 브라우저 환경에서 악성 링크가 담겨있는 페이지를 클릭하게 유도

➡️ 클릭하면 해커의 계좌로 돈을 송금하는 요청을 은행에 보낸다.

 

http://wisebank.com/transfer?account_number=해커계좌번호&amount=100000$”>

 

 

▶️ POST 요청으로 CSRF 공격하기

 

비밀번호 변경에 사용되는 POST 사용

POST

http://wisebank.com/password/change

 

body

{password:user’s-new-password}

 

 

▶️ CSRF 막기

 

- CSRF 토큰 사용하기

서버측에서 CSRF 공격에 보호하기 위한 문자열을 유저의 브라우저 웹 앱에만 제공

 

- same-site cookie 사용하기

같은 도메인에서만 세션/쿠키를 사용할 있다.

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 언제나 환영입니다.

 

 

 

 

 

 

 

 

HTTPS

 

 

HTTPS = HTTP + Secure

 

HTTP 프로토콜 내용을 암호화 ➡️ 보안성 추가

(기존 HTTP 방식은 전송도중 누군가 요청을 들여다보려고 하면 볼 수 있었다.)

 

 

➤ HTTPS 란?

 

  • Hyper Text Transfer Protocol Secure Socket layer
  • HTTP over SSL(TLS), HTTP over Secure 라고 부르기도 한다.
  • HTTP 요청을 SSL 혹은 TLS(Transfer Layer Security) 라는 알고리즘을 이용해 HTTP 통신을 하는 과정에서 데이터를 암호화해서 전송하는 방식이다.

 

 

 

➤ HTTPS의 목적

 

 

▶️ 암호화

 

  • 제3자가 서버와 클라이언트가 주고받는 데이터를 탈취하여 알아볼 수 없도록 한다.
  • 서버와 클라이언트가 서로 합의한 방법으로 데이터를 암호화하여 주고받는다.
  • 비대칭키 방식과 대칭키 방식을 혼용해서 사용

 

 

 

✅ 대칭키와 비대칭키

 

 

▶️ 대칭키

 

 

  • 양쪽이 공통의 비밀 키를 공유하여 데이터를 암호화 및 복호화하는 것을 말한다.
  • 암호화와 복호화에 같은 암호키를 사용하는 알고리즘이다.

 

 

▶️ 비대칭키

 

비대칭키

 

  • 암호화할 때와 복호화할 때의 키가 서로 다른 키를 가진 방식을 말한다.
  • 대칭키 알고리즘보다 훨씬 복잡하다.
  • 공개키(public key)와 개인키(private key)가 하나의 쌍을 이룬다.
  • 공개키 : 다른 사람들에게 공개된 키로 정보를 암호화할 수 있다.
  • 개인키 : 사용자 본인만 알고 있어서 암호를 풀 수 있다.
  • 공개키로 암호화하는 경우는 데이터 보안에 중점을 둔 것
  • 개인키로 암호화하는 경우는 전자서명같은 인증 과정에 중점을 둔 것
  • 비대칭키 대표 알고리즘: RSA, Diffie-Hellman(디피-헬만), 타원곡선암호(ECDSA)

 

 

 

▶️ 인증서

 

  • 브라우저가 서버의 응답과 함께 전달된 인증서를 확인할 수 있다.
  • 인증서 : 서버의 신원을 보증하여 접속한 사이트가 해커가 정교하게 따라한 가짜 사이트가 아님을 보장해주는 역할
  • Certificate Authority(CA) : 인증서를 발급해주는 엄격하게 공인된 기관, 보증할 수 있는 제3자
  • 서버의 공개키와 정보를 CA의 비밀키로 암호화하여 인증서를 발급한다.
  • 서버가 클라이언트에 CA에서 발급받은 인증서를 전달 ➡️ 클라이언트는 OS또는 브라우저에 미리 내장되어 있던 CA 리스트를 통해 인증된 CA에서 발급받은 인증서인지 먼저 확인한다.
  • 인증된 CA에서 발급한 인증서가 아니라면 브라우저에서 “NET::ERR_CERT_AUTHORITY_INVALID” 경고창을 띄워 서버와 연결이 안전하지 않다고 알려준다.
  • 클라이언트에서 인증서 확인 ➡️ 브라우저에 제공된 해당 CA 기관의 공개키로 서버 인증서를 복호화
  • 서명을 복호화해 얻은 공개키로 클라이언트가 서버를 믿을만한 대상인지 신뢰할 수 있다.
  • 만약 위조된 인증서라면 CA의 공개키로 인증서를 복호화할 수 없다. ➡️ 중간자 공격을 예방할 수 있다.
  • 서버와 클라이언트 간의 CA를 통해 서버를 인증하는 과정과 데이터를 암호화하는 과정을 아우른 프로토콜을 TLS 또는 SSL 이라고 한다. (SSL과 TLS는 동일한 규약을 뜻하지만 SSL이 표준화되며 바뀐 이름이 TLS 이다.)

 

인증서 예시 (google.com)

 

 

※ 중간자 공격(Man In The Middle Attack)

 

중간자 공격(man in the middle attack, MITM)은 네트워크 통신을 조작하여 통신 내용을 도청하거나 조작하는 공격 기법이다.

통신을 연결하는 두 사람 사이에 중간자가 침입하여 두사람은 상대방에게 연결했다고 생각하지만 실제로는 두 사람은 중간자에게 연결되어 있으며 중간자가 한쪽에서 전달된 정보를 도청 및 조작한 후 다른 쪽으로 전달한다.

암호 프로토콜은 중간자 공격을 막기 위해 인증을 사용한다.

예를 들어 TLS/SSL 프로토콜은 공개키를 기반으로 한 인증을 사용한다.

 

 

▶️ 자바에서 지원하는 인증서 형식

 

1. PKCS12 (Public Key Cryptographic Standards #12) : 여러 인증서와 키를 포함할 수 있으며 암호로 보호된 형식이다. 업계에서 널리 사용한다.

2. JKS(Java KeyStore) : PKCS12와 유사하다. 독점 형식이며 Java 환경으로 제한된다.

 

 

 

 

Hashing

 

 

➤ Hashing 이란?

 

어떠한 문자열에 임의의 수학적 연산(알고리즘)을 적용하여 다른 문자열로 변환하는 것

➡️ 제3자(해커)가 DB를 해킹하여 탈취한 데이터의 원본을 알 수 없게 만들어 데이터를 보호한다. 

 

대표적 해시 알고리즘 : SHA1, SHA256

 

 

➤ Encryption(암호화)

 

일련의 정보를 임의의 방식을 사용하여 다른 형태로 변환하여 해당 방식에 대한 정보를 소유한 사람을 제외하고 이해할 수 없도록 알고리즘을 이용해 정보를 관리하는 과정

 

 

➤ Hashing을 적용할 때 철칙

 

1. 모든 값에 대해 해시 값을 계산하는데 오래걸리지 않아야 한다.

  • 해시값을 해독(decoding)할 때는 많은 시간이 걸려야하지만 해시값을 만들 때는 오래걸리지 않아야 한다.

 

2. 최대한 해시 값을 피해야 하며, 모든 값은 고유한 해시값을 가진다.

  • Hashing은 어떠한 수학적 연산을 통해 문자열을 변환시켜주기 때문에 극히 낮은 확률로 전혀 다른 문자열임에도 똑같은 해시값을 가지는 경우가 존재한다.
  • 이러한 상황이 최대한 발생하지 않도록 하는 알고리즘들이 이미 배포되어 있기 때문에 그 알고리즘들을 사용할 수 있다.

 

3. 아주 작은 단위의 변경이라도 완전히 다른 해시 값을 가져야 한다.

  • 부분이 변경되어도 해시값으로는 추측할 수 없도록 완전히 다른 값을 반환해야 한다.

 

 

 

 

➤ Authentication (인증)

 

회원가입 시 비밀번호와 같이 알려지면 안되는 정보는 해시 알고리즘을 이용해 복잡하고 긴 문자열(해시값)으로 변환하여 DB에 저장한다. 이후 인증이 필요한 요청이 들어오면 입력받은 비밀번호를 해시값으로 바꾸고 DB에 저장되어있는 해시값과 비교한다. 

 

 

➤ Salt

 

암호화해야 하는 값에 어떤 ‘별도의 값’을 추가하여 결과를 변형하는 것

 

암호화만 해놓았을 때 안전해보이지만 특정 알고리즘을 통한 해시는 결과가 항상 같기 때문에 해시된 값과 원본 값을 대조한 테이블(레인보우 테이블)이 있다면 쉽게 원본 비밀번호를 알아내는 것이 가능하다.

➡️ 이를 보완하기 위해 기존 문자열에 개발자만 아는 Salt값을 추가한 후 Hashing 한다면 해커가 예상하기 힘든 전혀 다른 문자열이 만들어진다.

Salt를 이용하면 Hashing 만 했을 때보다 더욱 강력하고 안전하다.

 

 

※ 참고) 레인보우 테이블

  • 해시함수(MD-5, SHA-1, SHA-2 등)를 사용하여 만들어낼 수 있는 값들을 대량으로 저장한 표이다.
  • 해시함수를 이용하여 저장된 비밀번호로부터 원래 비밀번호를 추출하는데 사용한다.

 

 

 

▶️ Salt 사용 시 주의점

 

1. Salt 는 유저와 패스워드 별로 유일한 값을 가져야 한다.

2. 사용자 계정을 생성할 때와 비밀번호를 변경할 때마다 임의의 Salt를 사용해서 Hashing해야 한다.

3. Salt는 절대 재사용하지 말아야한다.

4. Salt는 DB의 유저 테이블에 같이 저장되어야 한다.

 

 

 

 

Cookie

 

 

 

➤ Cookie 란?

 

어떤 웹사이트에 들어갔을 때, 서버가 일방적으로 클라이언트에 전달하는 작은 데이터

 

  • 서버가 웹 브라우저에 정보를 저장하고 불러올 수 있는 수단
  • 해당 도메인에 대해 쿠키가 존재하면, 웹 브라우저는 도메인에게 http 요청시 쿠키를 함께 전달한다.
  • 쿠키를 이용하는 것 : 서버에서 클라이언트에 쿠키를 전송하는 것 + 클라이언트가 서버로 쿠키를 전송하는 것
  • 데이터(쿠키)를 저장한 이후 아무때나 가져올 수는 없고 저장한 이후 특정 조건들이 만족하는 경우에만 다시 가져올 수 있다. ➡️ 쿠키 옵션으로 설정
  • HTTP 요청은 stateless (무상태) 이지만 상태를 유지할 수 있게 해준다.

 

 

➤ Cookie 이용법

 

사용자 선호, 테마 등 장시간 보존해야하는 정보 저장에 적합하다.

로그인을 위한 인증정보를 저장하기도 한다.

 

인증정보 같이 민감한 정보는 Hashing 되어서 저장된다.

 

 

웹사이트에 들어갔을 때 “장치에 쿠키를 저장하시겠습니까?

➡️ 회사가 필요한 마케팅 정보등을 쿠키에 저장하는 경우도 많다.

 

 

➤ Cookie 전달 방법

 

서버가 응답 헤더에 Set-Cookie 라는 property에 쿠키의 이름, 값, 경로 등의 옵션을 전달

클라이언트는 Set-Cookie 를 확인한다.

 

클라이언트는 매 요청시 마다 쿠키를 전달한다.

서버는 쿠키내용을 바탕으로 무언가 일을 한다.

Set-Coookie property에는 다양한 옵션을 담아서 전송할 수 있다.

 

 

➤ Cookie Options

 

▶️ Domain

 

도메인 옵션과 서버의 도메인이 일치할 때만 쿠키를 전송할 수 있다.

➡️ 서로 다른 도메인 주소에서 쿠키를 전송하는 것을 방지한다.

 

 

▶️ Path

 

서버와 요청의 세부경로가 일치하는 경우 쿠키 전송

세부경로 : 서버가 라우팅할 때 사용하는 경로

옵션에 설정된 path를 전부 만족하는 경우 요청하는 Path가 추가로 더 존재하더라도 쿠키를 서버에 전송할 수 있다.

명시하지 않으면 기본으로 ‘ / ‘ 으로 설정되어 있다.

 

 

▶️ MaxAge or Expires

 

쿠키가 유효한 기간을 정하는 옵션이다.

만약 쿠키가 영원히 남아있다면 탈취하기 쉬워지기 때문에 유효기간을 설정하는 것이 보안 측면에서 중요하다.

MaxAge : 앞으로 몇 초 동안 쿠키가 유효한지 설정하는 옵션이다.

Expires : 언제까지 유효한지 Date를 지정한다. 클라이언트의 시간을 기준으로 지정된 시간, 날짜를 초과하면 쿠키는 자동으로 파괴된다.

 

위 옵션 여부에 따라 세션 쿠키(Session Cookie)와 영속성 쿠키(Persistent Cookie)로 나눠진다.

- 세션 쿠키 : MaxAge 또는 Expires 옵션이 없는 쿠키, 브라우저가 실행 중일 때만 사용할 수 있는 임시 쿠키이다. 브라우저를 종료하면 해당 쿠키는 소멸된다.

- 영속성 쿠키 : 브라우저의 종료 여부와 상관없이 MaxAge 또는 Expires에 지정된 유효시간만큼 사용가능한 쿠키이다.

 

 

▶️ HttpOnly

 

true : 자바스크립트에서 쿠키에 접근이 불가능

default : false 로 지정

쿠키는 <script> 태그로 접근 가능 ➡️ XSS 공격에 취약

HttpOnly 옵션을 주면 스크립트 태그로 접근을 불가능하게 만들어서 보안을 향상시킬 수 있다.

 

 

▶️ Secure

 

쿠키를 전송해야 할 때 사용하는 프로토콜에 따른 쿠키전송 여부를 결정한다.

true : HTTPS 프로토콜을 이용한 통신의 경우에만 쿠키를 전송할 수 있다.

 

 

▶️ SameSite

 

CORS 요청의 경우 옵션 및 메서드에 따라 쿠키 전송 여부 결정

CSRF 공격을 막는데 효과적인 옵션

 

Cross-Origin 요청을 받은 경우 요청에서 사용한 메서드와 해당 옵션(GET, POST, PUT, PATCH, ...)의 조합으로 서버의 쿠키 전송 여부를 결정하게 된다.

사용가능한 옵션

  • Lax : Cross-Origin 요청이면 ‘GET’ 메서드에 대해서만 쿠키를 전송할 수 있다.
  • Strict : Cross-Origin 이 아닌 same-site 인 경우에만 쿠키를 전송할 수 있다.
  • None : 모든 메서드 요청에 대해 쿠키 전송 가능, none 옵션은 Secure 쿠키 옵션이 필요하다.

same-site 는 요청을 보낸 origin과 서버의 도메인, 프로토콜, 포트가 같은 경우를 말한다. 이중 하나라도 다르면 Cross-Origin으로 구분된다.

 

 

➤ 쿠키를 이용한 상태 유지

 

서버는 쿠키의 특성을 이용하여 클라이언트에 인증정보를 담은 쿠키를 전송하고 클라이언트는 전달받은 쿠키를 요청과 같이 전송하여 Stateless 한 HTTP 통신을 Stateful 하게 유지할 수 있다.

하지만 쿠키는 오랜시간 유지될 수 있고, 자바스크립트를 이용해 쿠키에 접근할 수 있기 때문에 민감한 정보를 담는 것은 위험하다.

인증정보를 탈취하여 서버에 요청을 보낸다면 서버는 누가 요청을 보낸 건지 상관하지 않고 인증된 유저의 요청으로 보기 때문에 개인정보같은 민감한 정보에 접근이 가능하다.

 

 

 

※ 참고) CSRF(사이트 간 요청 위조, Cross-Site Request Forgery)

 

  • 웹사이트 취약점 공격중 하나
  • 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 해킹 공격이다.
  • 특정 웹사이트가 사용자의 웹 브라우저를 신용하는 상태를 노린 공격이다.
  • 사용자가 웹사이트에 로그인한 상태에 사이트 간 요청 위조 스크립트가 삽입된 페이지를 열면 공격 대상이 되는 웹사이트는 위조된 공격 명령이 믿을 수 있는 사용자로부터 발송된 것으로 판단하게 되어 공격에 노출된다.
  • CSRF는 개별 링크와 폼이 사용자 별로 예측 가능한 토큰을 사용할 때 발생한다. 예측 불가능한 토큰이 있다면 공격자는 요청 메시지를 변조할 수 없지만 예측 가능한 토큰이 있다면 요청 메시지를 변조할 수 있다.
  • 대안 : 리퍼러(Refere) 체크, 시큐리티 토큰(Security Token) 사용, 더블 서밋 쿠키(Double Submit Cookie) 검증

 

 

 

※ 참고) XSS(cross-site scripting, 사이트 간 스크립팅)

 

  • 웹 애플리케이션에서 많이 나타나는 취약점
  • 웹사이트 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다.
  • 주로 여러 사용자가 보는 전자 게시판에 악성 스크립트가 담긴 글을 올리는 형태로 이루어진다.
  • 웹 애플리케이션이 사용자로부터 입력받은 값을 제대로 검사하지 않고 사용할 경우 나타난다.
  • 이 취약점은 해커가 사용자의 쿠키, 세션 등의 정보를 탈취하거나 자동으로 비정상적인 기능을 수행하게 할 수 있다.
  • 대안 : 입출력 값 검증 및 무효화(XSS 공격은 기본적으로 <script> 태그를 사용하기 때문에 태그 문자 등 위험한 문자입력 시 문자 참조로 필터링하고 서버에서 브라우저로 전송시 문자를 인코딩 한다.)

 

 

 

※ 참고) CORS (교차 출처 리소스 공유, Cross-Origin Resource Sharing)

 

  • 추가 HTTP 헤더를 사용하여 한 출처(도메인, 프로토콜, 포트)에서 실행중인 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다.
  • 웹 애플리케이션은 리소스가 자신의 출처와 다를 때 교차 출처 HTTP 요청을 실행한다.

 

 

 

※ 참고) same-origin

 

같은 origin 은 URI 스키마, host name, 포트 넘버가 같은 것을 말한다.

클라이언트 측면에서 데이터의 기밀성과 일관성을 잃지 않기 위해서 관련없는 사이트가 공급하는 컨텐츠 사이에 엄격하게 분리한다.

 

 

Set-Cookie 참고자료

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie

 

Set-Cookie - HTTP | MDN

The Set-Cookie HTTP response header is used to send a cookie from the server to the user agent, so that the user agent can send it back to the server later. To send multiple cookies, multiple Set-Cookie headers should be sent in the same response.

developer.mozilla.org

 

 

 

리눅스 명령어

xargs

https://ko.linux-console.net/?p=229

 

초보자를위한 Linux Xargs 명령어의 12 가지 실용 사례

초보자를위한 Linux Xargs 명령어의 12 가지 실용 사례 Xargs 는 표준 입력에서 데이터 스트림을 읽고 명령 줄을 생성하고 실행하는 훌륭한 명령입니다. 이는 명령의 출력을 취하여 다른 명령의 인수

ko.linux-console.net

 

 

 

 

 

 

 

Spring에서의 트랜잭션

 

 

트랜잭션은 크게 로컬 트랜잭션분산 트랜잭션으로 구분된다.

 

Spring에서 사용되는 트랜잭션 방식은 선언형 트랜잭션 방식프로그래밍 코드 베이스 트랜잭션 방식이 있다.

 

트랜잭션은 애플리케이션 내에서 핵심 로직이 아닌 부가 기능이기 때문에 AOP 적용 대상이다.

➡️ 프로그래밍 코드 베이스 방식으로 트랜잭션을 적용하는 방식은 적절하지 않다.

 

 

 

Spring에서 선언형 방식의 트랜잭션 적용

 

Spring에서 선언형 방식으로 트랜잭션을 적용하는 방식은 크게 두가지가 있다.

1. 작성한 비즈니스 로직에 애너테이션을 추가

2. AOP 방식을 이용해서 비즈니스 로직에서 아예 트랜잭션 적용 코드를 감추는 방식

 

 

 

Spring Boot에서의 트랜잭션 설정

 

트랜잭션은 데이터베이스 인터랙션과 관련이 있기 때문에 커넥션 정보를 포함하고 있는 Datasource 가 기본적으로 필요하다.

Spring에서 트랜잭션은 기본적으로 PlatformTransactionManager 인터페이스에 의해 관리된다.

PlatformTransactionManager를 구현해서 유연하게 트랜잭션을 적용할 수 있다.

JPA 데이터 액세스 기술은 PlatformTransactionManager의 구현 클래스인 JpaTransactionManager를 사용한다.

 

 

 

 

애너테이션 방식의 트랜잭션 적용

 

@Transactional

Spring에서 가장 간단하게 트랜잭션을 적용할 수 있는 방법이다.

클래스에 추가하면 모든 메서드에 적용되서 어떤 메서드든 호출하면 하나의 새로운 트랜젝션이 생성된다.

rollback 없이(에러나 예외 없이) 메서드 호출이 끝나면 트랜젝션에서 commit이 일어나고 트랜젝션이 종료된다.

중간에 예외가 발생할 경우 rollback 되서 데이터베이스가 변경됐던 부분이 반영되지 않는다.

 

 

▶️ JPA 로그 레벨 설정

 

application.yml(resources 디렉토리에 있다.)에 해당 코드 추가

 

logging:
  level:
    org:
      springframework:
        orm:
          jpa: DEBUG

 

로그 레벨을 ‘DEBUG’ 레벨로 설정 ➡️ JPA 내부에서 ‘DEBUG’ 로그 레벨을 지정한 부분의 로그를 확인할 수 있다.

 

 

✔️ 참고) 로그 레벨

 TRACE  <  DEBUG  <  INFO  <  WARN  <  ERROR

  • ERROR : 요청을 처리하는 중 오류가 발생한 경우 표시한다.
  • WARN : 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 표시한다.
  • INFO : 상태변경과 같은 정보성 로그를 표시한다.
  • DEBUG : 프로그램을 디버깅하기 위한 정보를 표시한다.
  • TRACE : Debug보다 훨씬 상세한 정보를 나타낸다.

 

 

 

체크 예외(checked exception)는 rollback이 잘 될까?

 

체크 예외 예시 : Exception, SQLException, DataFormatException,...

체크 예외(checked exception)는 @Transactional 애너테이션만 추가해서는 rollback 되지 않는다.

체크 예외는 캐치(catch)한 후에 어떻게 처리할 지 결정해야 한다.

만일 예외 전략을 짤 필요가 없다면

@Transactional(rollbackFor = {SQLException.class, DataFormatException.class})

같이 체크 예외를 직접 지정해주거나 언체크 예외(unchecked exception)로 감싸서 rollback을 수행한다.

 

 

 

▶️ 메서드 레벨에 @Transactional 적용

 

메서드 레벨에도 @Transactional 에너테이션을 적용할 수 있다.

메서드 위에 작성하고 작성한 메서드만 트랜젝션을 적용한다.

 

Attribute

  • readOnly
    • 값으로 true 를 주면 읽기 전용 메서드가 된다.
    • commit 절차는 진행한다.
    • JPA 내부적으로 영속성 컨텍스트를 flush 하지 않는다. (변경 감지를 위한 스냅샷 생성 x)
    • 내부적으로 불필요한 추가 동작을 줄여서 성능을 최적화 할 수 있다.

 

 

※ @Transactional (readOnly = true)

➡️ readOnly를 찾을 수 없다고 뜨는 경우

Transactional import를 javax.transaction 이 아닌

org.springframework.transaction.annotation.Transactional 에서 해야 한다.

두 개의 라이브러리에 tranasactional 애너테이션이 존재하므로 import 할 때 주의한다.

 

 

▶️ 클래스 레벨과 메서드 레벨의 트랜잭션 적용 순서

 

✔️ 클래스 레벨에만 @Transactional이 적용된 경우

➡️ 클래스 레벨의 @Transactional이 메서드에 일괄 적용된다.

 

✔️ 클래스 레벨과 메서드 레벨에 함께 적용된 경우

메서드 레벨의 @Transactional이 적용된다.

만약 메서드 레벨의 @Transactional이 적용되지 않았다면 클래스 레벨의 @Transactional 이 적용된다.

 

 

 

➤ 트랜잭션 전파(Transaction Propagation)

 

@Transactional(propagation = Propagation.REQUIRED)

 

트랜잭션 Propagation?

트랜잭션의 경계에서 진행 중인 트랜잭션이 존재할 때 또는 존재하지 않을 때, 어떻게 동작할 것인지 결정하는 방식을 의미한다.

 

propagation attribute를 통해 설정할 수 있다.

 

▶️ propagation의 대표적 유형

1. Propagation.REQUIRED

propagation 중 가장 일반적으로 사용된다.

현재 진행 중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 존재하지 않으면 새 트랜잭션을 생성하도록 해준다.

 

2. Propagation.REQUIRES_NEW

이미 진행 중인 트랜잭션과 무관하게 새로운 트랜잭션이 시작된다.

기존에 진행중이던 트랜잭션은 새로 시작된 트랜잭션이 종료할 때까지 중지된다.

 

3. Propagation.MANDATORY

진행 중인 트랜잭션이 없으면 예외를 발생시킨다.

 

4. Propagation.NOT_SUPPORTED

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행 중인 트랜잭션이 있으면 메서드 실행이 종료될 때까지 진행중인 트랜잭션은 중지하며, 메서드 실행이 종료되면 트랜잭션을 계속 진행한다.

 

5. Propagation.NEVER

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행중인 트랜잭션이 존재할 경우에 예외를 발생시킨다.

 

 

 

➤ 트랜잭션 격리 레벨(Isolation Level)

 

트랜잭션은 다른 트랜잭션에 영향을 주지 않고, 독립적으로 실행되어야 하는 격리성이 보장되어야 한다.(ACID 원칙 중 Isolation)

트랜잭션 Isolation은 동시 트랜잭션에 의해 적용된 변경 사항이 서로에게 어떻게 보이는지 설정한다.

각각의 isolation 레벨은 트랜잭션이 동시에 발생할 때 생길 수 있는 아래 문제들을 예방한다.

  1. Dirty read : 동시 트랜잭션에서 커밋되지 않은 변화를 읽을 수 있다.
  2. Nonrepeatable read : 동시 트랜잭션이 동일한 행을 업데이트하고 커밋하는 경우 행을 다시 읽을 때 다른 값을 얻는다.
  3. Phantom read : 다른 트랜잭션이 범위의 일부 행을 추가하거나 제거하고 커밋하는 경우 쿼리를 다시 실행한 후 다른 행을 가져온다.

Spring 에서 @Transactional 의 attribute로 isolation을 통해 트랜잭션 간의 격리성을 제공한다.

 

1. Isolation.DEFAULT

데이터베이스에서 제공하는 기본 값

 

2. Isolation.READ_UNCOMMITTED

다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것을 허용한다.

 

3. Isolation.READ_COMMITTED

다른 트랜잭션에 의해 커밋된 데이터를 읽는 것을 허용한다.

 

4. Isolation.REPEATABLE_READ

트랜잭션 내에서 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록 한다.

 

5. Isolation.SERIALIZABLE

동일한 데이터에 대해서 동시에 두 개 이상의 트랜잭션이 수행되지 못하도록 한다.

 

 

https://www.baeldung.com/spring-transactional-propagation-isolation

 

Transaction Propagation and Isolation in Spring @Transactional | Baeldung

Learn about the isolation and propagation settings in Spring's @Transactional

www.baeldung.com

 

 

 

 

 

AOP 방식의 트랜잭션 적용

 

 

1. AOP 방식으로 트랜잭션 적용을 위한 Configuration 클래스 정의

@Configuration 애너테이션 추가

 

2. TransactionManager DI

애플리케이션에 트랜잭션 적용하기 위해서는 TransactionManager라는 객체가 필요하다.

DI(의존성 주입)를 통해 TransactionManager 객체를 받는다.

 

3. 트랜잭션 advice용 TransactionInterceptor 빈 등록

Spring에서 TransactionInterceptor를 이용해서 대상 클래스 / 인터페이스에 트랜잭션 경계를 설정하고 트랜잭션을 적용할 수 있다.

  • 트랜잭션 attribute 지정
  • 트랜잭션을 적용할 매서드에 트랜잭션 애트리뷰트 매핑
  • TransactionInterceptor 객체 생성

 

4. Advisor 빈 등록

  • 포인트 컷 지정
  • Advisor 객체 생성

 

 

 

 

✅ 추가 학습이 필요한 부분

 

✔️ 분산 트랜잭션

서로 다른 데이터 소스를 사용하는 한 개 이상의 데이터베이스를 하나의 트랜잭션으로 묶어서 처리해야 하는 것을 분산 트랜잭션이라고 한다.

분산 트랜잭션을 적용하면 서로 다른 리소스에 대한 작업을 수행하더라도 하나의 작업처럼 관리하기 때문에 원자성을 보장할 수 있다.

WAS가 제공하는 JTA(Java Transaction API) 서비스 이용

Atomikos는 Spring Boot에서 스타터로 지원하는 가장 인기있는 오픈 소스 트랜잭션 관리자이다.

 

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😄

 

 

 

 

 

트랜잭션(Transaction)이란?

 

 

트랜잭션 : 여러개의 작업들을 하나의 그룹으로 묶어서 처리하는 처리 단위

두 개 이상의 작업들이 그룹처럼 묶여서 처리될 수 있는데 하나라도 실패할 경우 애플리케이션의 신뢰성이 깨어지는 상황이 발생할 수 있다.

애플리케이션에서 신뢰성이 깨어지는 상황이 발생하면 트랜잭션이라고 부를 수 없다.

 

물리적으로는 여러 개의 작업이지만 논리적으로는 마치 하나의 작업으로 인식하는 경우

전부 성공 또는 전부 실패 (All or Nothing)로만 처리되어야 트랜잭션의 의미를 가진다.

 

 

 

➤ ACID 원칙

 

데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다.

 

 

1. 원자성 (Atomicity)

트랜잭션에서 작업을 더 이상 쪼갤 수 없다는 것을 의미한다.

모두 성공하거나 모두 실패하거나(All or Nothing) 

 

2. 일관성 (Consistency)

트랜잭션이 에러없이 성공적으로 끝날 경우, 비즈니스 로직에서 의도하는대로 일관성있게 저장되거나 변경되는 것을 의미한다.

 

3. 격리성 (Isolation)

여러 개의 트랜잭션이 실행될 경우 각각 독립적으로 실행되어야 함을 의미한다.

 

4. 지속성 (Durability)

트랜잭션이 완료되면 그 결과는 지속되어야 한다는 의미이다.

 

 

 

➤ 트랜잭션 커밋(commit)과 롤백(rollback)

 

 

▶️ commit

  • 모든 작업을 최종적으로 데이터베이스에 반영하는 명령어
  • 커밋을 수행하면 변경된 내용이 DB에 영구적으로 저장된다.
  • 커밋 명령을 수행하면 하나의 트랜젝션 과정은 종료하게 된다.

 

 

▶️ rollback

  • 작업 중 하나라도 문제가 발생했을 경우 트랜잭션 내에서 수행된 작업들을 취소한다.
  • 트랜잭션 시작 이전의 상태로 되돌아간다.

 

 

 

➤ JPA의 Entity Transaction commit() 내부 들여다보기

 

EntityTransaction 객체 commit() 메서드 호출 시 어떤 일이 일어날까?

 

EntityTransaction 인터페이스의 구현 클래스인 TransactionImpl 클래스의 commit()을 호출한다.

-> TransactionDriverControlImpl (물리적인 트랜잭션을 제어하기 위한 로컬 트랜잭션 드라이버 구현 객체) 를 얻은 후 구현 메서드인 commit()을 다시 호출한다.

-> TransactionDriverControlImpl 에서 JDBC Connection의 액세스 방법을 제공하는 JdbcResourceTransaction의 구현객체인 AbstractLogicalConnectionImplementor 의 commit()을 호출한다. (여기까지가 Hibernate ORM에서의 영역)

-> 물리적인 JDBC Connection을 통해 데이터베이스와 인터랙션하기 위해서 JDBC API의 구현체인 JdbcConnection 영역으로 이동한다.

-> (여기부터 JDBC API의 구현체인 H2의 영역) 데이터베이스에 commit 명령을 준비(prepareCommand)한 후, 명령을 실행(executeUpdate)한다.

-> Command 클래스에서 메서드 실행된다.

데이터베이스에 commit 명령을 전달하기 위해 commitIfNonTransactional 메서드 호출한다.

commitIfNonTransactional() 메서드 내부에서 auto commit 여부를 체크한 후

데이터베이스 세션에 해당하는 Session 객체를 통해 commit 명령을 수행한다.

만약에 commitIfNonTransactional() 수행 과정에서 예외가 발생하면 rollback을 수행한다.

-> SessionLocal 클래스에서 트랜잭션에 대한 commit이 수행된다.

 

 

 

 

 

 

 

읽어주셔서 감사합니다.

오개념에 대한 지적은 늘 환영입니다.

 

 

 

 

 

 

 

 

Spring Data JPA

 

Spring Data JDBC와 Spring Data JPA는 모두 Spring Data라는 프로젝트에서 지원하는 PSA(일관된 서비스 추상화)가 적용되어 있기 때문에 사용법이 거의 동일하다.

 

Spring Data JPA는 Spring Data 패밀리 기술 중 하나이다.

JPA 기반의 데이터 액세스 기술을 좀 더 쉽게 사용할 수 있게 해준다.

 

 

➤ JPA vs Hibernate ORM vs Spring Data JPA

 

JPA

  • 엔터프라이즈 Java 애플리케이션에서 관계형 데이터베이스를 사용하기 위해 정해 놓은 표준 스펙(사양 또는 명세, Specification)

Hibernate ORM

  • JPA의 표준 스펙을 구현한 구현체, 실제로 사용할 수 있는 API라고 보면 된다.

Spring Data JPA

  • JPA 스펙을 구현한 구현체의 API(일반적으로 Hibernate ORM)을 조금 더 쉽게 사용할 수 있도록 해주는 모듈이다.

 

 

 

 

Repository에 JPA 기능 추가하기

 

엔티티 클래스들 정의 -> 엔티티 클래스 매핑 -> repository 인터페이스 작성

 

repository 인터페이스에서 spring data jdbc와 달라진 점 : CrudRepository를 상속하는 대신 JpaRepository를 상속한다.

JpaRepository : CrudRepository보다 JPA에 특화된 더 많은 기능을 포함하고 있다.

 

 

▶️ JPQL을 통한 객체 지향 쿼리 사용

 

  • JPA에서는 JPQL이라는 객체 지향 쿼리를 통해 데이터베이스 내의 테이블을 조회할 수 있다.
  • JPQL은 엔티티 클래스 객체를 대상으로 객체를 조회하는 방법이다.  (데이터베이스의 테이블을 대상으로 조회 x)
  • JPQL의 문법을 사용해서 객체를 조회하면 JPA가 내부적으로 JPQL을 분석해서 적절한 SQL을 만든 후 데이터베이스를 조회하고 조회한 결과를 엔티티 객체로 매핑한 뒤 반환한다.
  • ‘SELECT c’와 같이 별칭으로 생략한 형태로 사용 가능하다.

 

 

▶️ 네이티브 SQL을 통한 조회

 

  • Spring Data JDBC와 JPA는 네이티브 SQL 쿼리를 작성해서 사용할 수 있다.
  • nativeQuery 애트리뷰트 값을 true로 설정하면 value 애트리뷰트에 작성한 SQL 쿼리가 적용된다.

 

 

➤ Spring Data JDBC와 Spring Data JPA의 @Query 차이

 

둘다 애너테이션 이름은 같지만 패키지 자체가 다르다.

➡️ Starter 모듈이 둘 다 의존 라이브러리에 포함되어 있는 경우 패키지 경로를 혼동하지 않도록 주의한다.

 

Spring Data JDBC의 @Query 애너테이션 패키지 경로

import org.springframework.data.jdbc.repository.query.Query

 

Spring Data JPA의 @Query 애너테이션 패키지 경로

org.springframework.data.jpa.repository.Query

 

 

 

 

 

 

읽어주셔서 감사합니다.

오개념에 대한 지적은 늘 환영입니다.

 

 

 

 

 

JPA 엔티티(Entity) 매핑

 

 

➤ 엔티티 매핑

 

JPA의 매핑 애너테이션을 이용하면 데이터베이스의 테이블과 엔티티 클래스를 매핑할 수 있다.

 

 

▶️ @Entity

  • 엔티티 클래스와 테이블을 자동으로 매핑해준다.
  • 이 애너테이션을 붙이면 JPA 관리 대상 엔티티가 된다.
  • name attribute를 설정하지 않으면 기본적으로 클래스명과 같은 테이블 명을 매핑한다.
  • 테이블 명과 엔티티 클래스 명이 다를 경우 name attribute를 설정해주어야 한다.

 

 

▶️ @Table

  • 데이터베이스의 테이블 이름을 설정할 때 사용한다.
  • name 애트리뷰트를 설정하지 않으면 기본값으로 클래스 이름을 테이블 이름으로 사용한다.
  • @Table 은 옵션이고 추가하지 않을 경우 클래스 이름을 테이블 이름으로 사용한다.

 

 

 

⚠️ 주의 사항

  • @Table ➡️ 옵션
  • @Entity, @Id ➡️ 필수
  • 파라미터가 없는 기본 생성자는 필수로 추가해주자

 

 

➤ 기본키 매핑

 

데이터베이스 테이블에 기본키 설정은 필수이다.

JPA에서 기본키 매핑은 @Id 를 필드에 추가해주면 되는데 JPA에서 어떤 방식으로 기본키를 생성할 것인지에 대한 다양한 전략을 지원해준다.

 

✅ 기본키 직접 할당

애플리케이션 코드 상에서 기본키를 직접 할당해주는 방식이다.

 

 

✅ 기본키 자동 생성

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;

 

  • IDENTITY
    • 기본키 생성을 데이터베이스에 위임한다.
    • 데이터베이스에서 기본키를 생성해주는 대표적인 방식은 MySQL의 AUTO_INCREMENT 기능으로 자동 증가 숫자를 기본키로 사용하는 방식이 있다.
  • SEQUENCE
    • 데이터베이스에서 제공하는 시퀀스를 사용해서 기본키를 생성하는 전략이다.
  • TABLE
    • 별도의 키 생성 테이블을 사용하는 전략이다.
  • AUTO
    • JPA가 데이터 베이스 Dialect에 따라서 적절한 전략을 자동으로 선택한다.

 

 

TABLE 전략은 키 생성 전용 테이블을 별도로 만들어야 되고, 키를 조회하고 업데이트할 때 쿼리를 전송해야하기 때문에 성능면에서 떨어진다.

 

https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-table

 

Hibernate ORM 5.6.10.Final User Guide

Fetching, essentially, is the process of grabbing data from the database and making it available to the application. Tuning how an application does fetching is one of the biggest factors in determining how an application will perform. Fetching too much dat

docs.jboss.org

 

 

 

▶️ 참고) Dialect란?

 

Dialect는 JDBC 타입과 SQL 타입 간에 연결하는 역할을 한다.

Dialect를 사용하면 Hibernate 가 특정 관계형 데이터베이스에 최적화된 SQL을 생성할 수 있다.

Hibernate Dialect 클래스를 기반으로 특정 데이터베이스에 대한 쿼리를 만든다.

hibernate dialect 는 프레임워크에 어떻게 hibernate 쿼리를 SQL 쿼리로 바꿀 지에 대한 정보를 제공해준다.

 

  • 적절한 SQL 쿼리를 생성한다.
  • 애플리케이션이 두 개 이상의 데이터베이스와 연결되어 있을 경우 특정 데이터베이스와 상호작용할 수 있다.
  • 구성 파일에 지정되지 않은 경우에도 데이터베이스 소프트웨어를 기반으로 hibernate 구성 파일 속성에 대한 기본값을 설정한다.

 

https://www.geeksforgeeks.org/hibernate-sql-dialects/

 

Hibernate - SQL Dialects - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

 

 

 

➤ 필드(멤버 변수)와 칼럼간의 매핑

 

 

▶️ @Column

 

  • 필드와 데이터베이스의 칼럼을 매핑해주는 애너테이션이다.
  • 이 애너테이션이 없고 필드만 정의되어 있다면 JPA는 기본적으로 이 필드가 테이블의 칼럼과 매핑되는 필드라고 간주하고 @Column 애너테이션에 사용되는 attribute 값은 모두 디폴트로 적용된다.

Attribute

  • nullable
    • default : true
    • 칼럼에 null을 허용할지 여부를 설정
  • updatable
    • 등록한 이후 칼럼 데이터를 수정할 수 있는지 여부를 지정한다.
    • default : true
  • unique
    • 하나의 칼럼에 unique 제약 조건을 설정한다. (중복 허용 x)
    • default : false
  • length
    • 칼럼에 저장할 수 있는 문자 길이를 지정한다.
    • default : 255
  • name
    • default : 필드 명
    • 필드와 매핑되는 칼럼의 이름이 필드명과 다를 경우 지정해 줄 수 있다.

 

 

▶️ @Temporal

 

  • java.util.Date, java.util.Calendar 타입을 매핑하기 위해서 이 애너테이션을 추가해야 한다.
  • LocalDate, LocalDateTime 타입일 경우는 생략가능하다.

 

 

 

▶️ @Transient

 

  • 이 애너테이션을 필드에 추가하면 테이블 칼럼과 매핑하지 않는다.
  • 데이터베이스에 저장하지 않고 조회할 때도 매핑되지 않는다.
  • 주로 임시 데이터를 메모리에서 사용하기위한 용도로 사용된다.

 

 

▶️ @Enumerated

 

  • enum 타입과 칼럼을 매핑할 때 사용한다.
  • Attribute
    • EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장한다.
    • EnumType.STRING : enum 이름을 테이블에 저장한다.

 

⚠️ 주의

EnumType.ORDINAL 로 지정할 경우

기존에 정의되어 있는 enum 사이에 새로운 enum을 추가한다면 그때부터 테이블에 이미 저장되어 있는 enum 순서 번호와 enum 에 정의되어 있는 순서가 일치하지 않게 되는 문제가 발생한다.

 

 

 

@Column doc

https://docs.oracle.com/javaee/7/api/

 

Java(TM) EE 7 Specification APIs

 

docs.oracle.com

 

 

 

 

 

엔티티 간의 연관관계 매핑

 

 

➤ 연관관계 매핑이란?

 

엔티티 클래스 간의 관계를 만들어 주는 것이다.

 

엔티티 간의 참조할 수 있는 객체수에 따라서

일대다(1:N), 다대일(N:1), 다대다(N:N), 일대일(1:1) 관계로 나눌 수 있다.

 

 

➤ 단방향 연관 관계

 

한 쪽 클래스만 다른 쪽 클래스의 참조 정보를 가지고 있는 관계

 

 

➤ 양방향 연관 관계

 

양 쪽 클래스 모두 서로의 객체를 참조 정보를 가지고 있는 관계

 

JPA는 단방향, 양방향 연관 관계 모두 지원하지만 Spring Data JDBC는 단방향 연관 관계만 지원한다.

 

 

➤ 일대다(1:N) 단방향 연관 관계

 

일(1)에 해당하는 클래스가 다(N)에 해당하는 객체를 List 등을 사용해서 참조할 수 있다.

일대다 단방향 매핑은 잘 사용하지 않는다.

 

 

➤ 다대일(N:1) 연관 관계

 

다(N)에 해당하는 클래스가 일(1)에 해당하는 객체를 참조할 수 있는 관계를 의미한다.

 

▶️ @ManyToOne

 

  • 이 애너테이션으로 다대일 관계를 명시한다.
  • @JoinColumn(name = “MEMBER_ID”)
    • 매핑하는 테이블의 외래키(부모 테이블의 기본키)에 해당하는 칼럼명을 적어준다.

 

 

➤ 일대다(1:N) 연관 관계

 

다대일 연관관계를 설정한 이후에 자식 테이블에도 일대다 관계를 만들어줘서 양방향 관계를 만들어 준다.

 

 

▶️ @OneToMany

 

  • 이 애너테이션을 부모테이블에 추가하여 일대다 관계를 만들어준다.

Attribute

  • mappedBy
    • 값은 관계를 소유하고 있는 필드(N(다)에 해당하는 필드)를 지정한다.
    • mappedBy는 참조할 대상이 있어야 한다.
    • 일대다 단방향 매핑의 경우에는 필요하지 않다.

 

 

➤ 다대다(N:N) 연관 관계

 

다대다는 관계는 중간에 테이블을 하나 추가해서 두 개의 일대다 관계로 만들어주는 것이 일반적이다.

 

 

➤ 엔티티 간의 연관 관계 매핑 권장 방법

 

  • 일대다 매핑은 사용하지 않는다.
  • 제일 먼저 다대일 단방향 매핑부터 적용한다.
  • 다대일 단방향 매핑을 통해 객체 그래프 탐색으로 조회할 수 없는 정보가 있을 경우, 양방향 매핑을 적용한다.

 

 

 

➤ Cascading

 

부모테이블에서 자식테이블로 데이터의 상태 변화를 전파(?)할 때 사용한다.

JPA javax.persistence.CacadeType 에 정의되어 있다.

부모 테이블의 @OneToMany 애너테이션에 Attribute로 추가한다.

 

@OneToOne(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private Stamp stamp;

 

Attribute

  • ALL
  • PERSIST
  • MERGE
  • REMOVE
  • REFRESH
  • DETACH

 

 

 

 

읽어주셔서 감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😄

 

 

 

 

 

 

 

 

 

JPA(Java Persistence API)란?

 

 

➤ JPA(Java Persistence API)란?

 

  • ORM(Object-Relation Mapping) 기술의 표준 사양(또는 명세, Specification)
  • Java의 인터페이스로 사양이 정의되어 있고 JPA라는 표준 사양을 구현한 구현체는 따로 있다.

 

 

➤ Hibernate ORM

 

  • JPA 표준 사양을 구현한 구현체 : Hibernate ORM, EcliipseLink, DataNucleus
  • Hibernate ORM은 JPA에서 정의해둔 인터페이스를 구현한 구현체 중 하나이다.
  • JPA에서 지원하는 기능 이외에도 자체적으로 사용하도록 지원하는 API 도 있다.

 

 

※ Jakarta Persistence

 

Java Persistence API 의 약자이지만 현재는 Jakarta Persistence 라고도 불린다.

2019년에 나온 JPA 3.0 버전 부터 Jakarta Persistence 라는 이름으로 바뀌었다.

Jakarta Persistence 라고 바뀌면서 패키지와 속성도 javax.persistence 에서 jakarta.persistence 로 이름이 바뀌었다.

엔터프라이즈 Java 애플리케이션에서 관계형 데이터 관리를 설명하는 Jakarta EE 애플리케이션 프로그래밍 인터페이스 사양(specification)이다.

 

 

 

➤ 데이터 액세스 계층에서의 JPA 위치

 

 

 

데이터 액세스 계층에서 JPA는 상단에 위치한다.

데이터 저장, 조회 등의 작업은 JPA를 거쳐 JPA의 구현체인 Hibernate ORM을 통해서 이뤄진다.

Hibernate ORM은 내부적으로 JDBC API를 이용해서 데이터베이스에 접근한다.

 

 

 

➤ JPA(Java Persistence API)에서 P(Persistence)의 의미

 

Persistence는 영속성, 지속성이라는 뜻이다.

그렇다면 무엇을 지속시키는 것일까?

엔티티 객체 정보를 지속시킨다는 의미이다.

 

 

▶️ 영속성 컨텍스트(Persistence Context)

 

영속성 컨텍스트(Persistence Context)

 

ORM(Object-relational-mapping)은 객체(Object)와 데이터베이스 테이블의 매핑을 통해 엔티티 클래스 객체 안에 포함된 정보를 테이블에 저장하는 기술이다.

JPA에서는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트(Persistence Context)라는 곳에 보관해서 애플리케이션 내에서 오래 지속되도록 한다.

엔티티 정보는 데이터베이스 테이블에 데이터를 저장, 수정, 조회, 삭제하는데 사용된다.

 

 

JPA API 중 엔티티 정보를 영속성 컨텍스트에 저장(persist)하는 API를 사용하면 영속성 컨텍스트의 1차 캐시에 엔티티 정보가 저장된다.

 

 

JPA 영속성 컨텍스트

  • EntityManager 클래스에 의해서 관리된다. -> EntityManagerFactory 객체를 Spring으로부터 DI 받을 수 있다.
  • EntityManagerFactory의 createEntityManager() 메서드를 이용해서 EntityManager 객체를 얻을 수 있다. -> JPA API 메서드 사용 가능
  • persist() 메서드: 영속성 컨텍스트에 객체의 정보들이 저장된다.
  • 저장되었는지 확인하려면 find(조회할 엔티티 클래스의 타입, 조회할 엔티티 클래스의 식별자 값) 메서드 사용

 

 

 

➤ JPA API로 영속성 컨텍스트 이해하기

 

 

▶️ JPA API를 사용하기 위한 사전 준비

 

 

✅ build.gradle 설정

 

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

Spring Data JPA 기술을 포함한 JPA API를 사용할 수 있다.

 

 

✅ application.yml에 아래 코드 추가

jpa:
	hibernate:
		ddl-auto: create  # (1) 스키마 자동 생성
	show-sql: true      # (2) SQL 쿼리 출력

 

※ yml에서 추가한 코드가 제대로 적용되지 않을 때는 공백 레벨을 잘 확인해보자.

 

Spring Data JDBC에서는 schema.sql 파일에 테이블 생성을 위한 스키마를 직접 작성해주어야 하지만 JPA는 (1)의 코드 하나를 추가함으로써 JPA가 자동으로 데이터베이스에 테이블을 생성해 준다.

 

 

▶️ @GeneratedValue

 

식별자를 생성해주는 전략을 지정할 때 사용

이 애너테이션을 엔티티의 멤버 변수에 추가하면 데이터베이스 테이블에서 기본키가 되는 식별자를 자동으로 설정해준다.

 

 

▶️ 영속성 컨텍스트에 저장하기

 

persist() 를 호출 -> 1차 캐시에 객체가 저장되고, 객체가 쓰기 지연 SQL 저장소에 INSERT 쿼리 형태로 등록이 된다.

영속성 컨텍스트에 객체를 저장하지만 실제 테이블에 정보를 저장하지는 않는다.

 

JPA가 내부적으로 테이블을 자동으로 생성하고, 테이블의 기본키를 할당해준다.

 

 

▶️ 데이터베이스 테이블에도 데이터 저장하기

 

EntityManagergetTrancsation() 메서드를 이용하여 Transaction 객체를 얻는다.

(JPA에서는 Transaction 객체를 기준으로 DB 테이블에 데이터를 저장한다.)

Transaction을 시작하기 위해서 begin() 메서드 사용

➡️ persist() 로 객체를 영속성 컨텍스트에 저장

➡️ commit() 메서드 호출하여 영속성 컨텍스트에 저장되어 있는 객체를 DB 테이블에 저장한다.

 

 

✔️ persist() : 영속성 컨텍스트의 1차 캐시에 엔티티 클래스의 객체가 저장 + ‘쓰기 지연 저장소’에 INSERT 쿼리가 등록

✔️ commit() : ‘쓰기 지연 SQL 저장소’에 등록된 모든 INSERT 쿼리가 실행 + 실행된 INSERT 쿼리는 쓰기 지연 SQL 저장소에서 제거됨

✔️ find() : 1차 캐시에서 해당 객체가 있는지 조회하고, 없으면 테이블에 SELECT 쿼리를 전송해서 조회

 

 

 

➤ 영속성 컨텍스트와 테이블에 엔티티 업데이트

 

find() 메서드로 1차 캐시에 저장된 객체를 불러와서 객체에 setter로 데이터를 변경

commit() 메서드로 SQL 저장소에 등록된 UPDATE 쿼리가 실행된다.

 

✅ setter 메서드로 값을 변경만 해도 commit() 시점에 UPDATE 쿼리가 실행되는 이유?

영속성 컨텍스트에 엔티티가 저장될 경우 저장되는 시점의 상태를 그대로 가지고 있는 스냅샷을 생성한다.

그 후 엔티티의 값을 setter 메서드로 변경한 후, commit()을 하면 변경된 엔티티와 이전에 이미 떠 놓은 스냅샷을 비교한 후 변경된 값이 있으면 쓰기 지연 SQL 저장소에 UPDATE 쿼리를 등록하고 UPDATE 쿼리를 실행

 

 

➤ 영속성 컨텍스트와 테이블의 엔티티 삭제

 

EntityManager의 remove() 메서드를 통해 1차 캐시에 있는 엔티티를 제거하도록 요청

Transaction의 commit() 을 실행하면 영속성 컨텍스트에 1차 캐시에 있는 엔티티를 제거하고, 쓰기 지연 SQL 저장소에 등록된 DELETE 쿼리가 실행된다.

 

 

 

▶️ EntityManager의 flush() API

 

transaction의 commit() 메서드를 호출하면 JPA 내부적으로 flush() 메서드가 호출되어 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.

 

 

 

※ 추가로 학습해야할 자료들

 

JPA와 Hibernate 에서 Entity Lifecycle Model

https://thorben-janssen.com/entity-lifecycle-model/

 

Entity Lifecycle Model in JPA & Hibernate

The entity lifecycle model is one of the core concepts of JPA. It defines if an entity gets loaded, if changes get persisted, and much more

thorben-janssen.com

 

 

 

 

읽어주셔서 감사합니다.

오개념에 대한 지적은 언제나 환영입니다. 😄

 

 

 

 

 

 

 

Spring Data JDBC를 통한 데이터 액세스 계층 구현 (서비스, 레포지토리 구현)

 

 

➤ Repository Interface

 

Spring Data JDBC, Spring Data JPA에서 데이터 액세스 계층에서 데이터베이스와 상호작용하는 역할을 하는 인터페이스를 repository라고 한다.

 

기본적으로 CrudRepository를 상속하는 repository 인터페이스 작성한다.

ex) public interface MemberRepository extends CrudRepository<Member, long> { }

 

CrudRepository 인터페이스를 통해 데이터를 데이터베이스 테이블에 저장, 조회, 수정, 삭제가 가능하다.

 

 

 

➤ 쿼리 메서드(Query Method)

 

Spring Data JDBC에서 쿼리 메서드(Query Method)를 지원한다.

 

✅ ‘find + By + SQL 쿼리문에서 WHERE 절의 칼럼명 + (WHERE 절 칼럼의 조건이 되는 데이터)’

형식으로 쿼리 메서드(Query Method)를 정의하면 조건에 맞는 데이터를 테이블에서 조회한다.

ex) findByName(String name);

 

리턴값으로 SQL 질의를 통한 결과 데이터를 받아서 엔티티 클래스의 객체로 지정해준다.

그리고 Spring Data JDBC 에서 Optional을 지원하기 때문에 리턴값을 Optional로 래핑해준다.

래핑해주는 이유는?

Optional을 사용해서 리턴값을 래핑하면 Service 클래스에서 이 Optional을 이용해서 코드를 더 효율적이고 간결하게 구성할 수 있다.

 

WHERE 절의 조건 칼럼을 여러 개 지정하고 싶다면 ‘And’를 사용하면 된다.

ex) findByPhoneAndName(String phone, String name);

 

쿼리 메서드에 작성한 칼럼명은 내부적으로 테이블의 칼럼명으로 바뀌지만

Spring JDBC 입장에서는 엔티티 클래스를 바라보고 작업한다.

반드시 엔티티 클래스의 멤버 변수명을 적어주어야 한다.

테이블의 칼럼명으로 적는다. ❌

 

(단어를 일치시키면 가장 좋지만 Java에서는 CamelCase 표기법을 사용하고 테이블 칼럼명은 언더스코어(_) 표기법을 사용하기 때문에 2 단어 이상 작성시 이름이 다를 수 있다.)

 

 

➤ @Query 메서드

 

쿼리 메서드명에 작성하지 않고 직접 쿼리문을 작성해서 질의를 할 경우 사용

Attribute로 쿼리문을 작성한다.

동적 쿼리 파라미터(named parameter)

콜론(:) 뒤에는 findBy~()의 괄호안에 동적 쿼리 파라미터(named parameter)를 작성해준다.

ex) @Query(“SELCET * FROM ORDER WHERE ORDER_ID = :orderId”);

 

단순한 쿼리의 경우 쿼리 메서드를 이용하는 것이 간결한 코드 유지와 생산성 면에서 바람직하다.

 

 

➤ Repository 인터페이스 Service 에서 사용하기

 

Service에서 DI를 주입해서 Repository 생성

 

Repository는 인터페이스인데 구현 클래스를 작성하지 않았지만 사용 가능하다.

Repository 인터페이스 구현 클래스는 Spring Data JDBC에서 내부적으로 Java 리플렉션 기술과 Proxy 기술을 이용해서 repository 인터페이스의 구현 클래스 객체를 생성해준다.

 

비즈니스 로직에서 어떤 검증이 필요한 로직은 검증하는 로직을 추출해서 메서드를 작성 후 호출한다. ➡️ 코드의 간결성, 가독성 향상

ex) 회원 정보 리소스를 데이터베이스에 Insert할 경우 이미 Insert된 리소스인지 여부를 검증하는 로직 분리 (findVerifiedMember)

 

 

✅ Optional.ofNullable(...)

파라미터로 전달받은 엔티티 클래스 객체는 클라이언트 쪽에서 선택적으로 수정할 수 있기 때문에 멤버 변수에 null 이 있을 수도 있다.

이처럼 멤버 변수 값이 null 일 경우

Optional.of()가 아닌 Optional.ofNullable()을 이용해서 null 값을 허용할 수 있다.

null 이더라도 NullPointerException이 발생하지 않고 원하는 다음 메서드를 호출할 수 있다.

ifPresent() 메서드를 이용해서 null 값이 아니라면 코드가 실행 되도록 작성한다. (람다식으로 작성)

이 때 값이 null 이라면 실행되지 않는다.

ex) Optional.ofNullable(coffee.getName).ifPresent(name -> findCoffee.setName(name));

 

 

✅ delete 를 사용하여 회원 정보 삭제 ?

실무에서는 회원 정보 자체를 테이블에서 삭제하기 보다 MEMBER_STATUS 같은 칼럼을 두어 상태 값만 변경한다.

회원의 회원 가입 상태를 ‘가입’ , ‘휴면’ , ‘탈퇴’ 등의 상태 정보로 나누어 관리하는 것이 바람직하다.

 

 

✅ orElseThrow()

Optional의 메서드인 orElseThrow()를 이용하면 해당 객체가 null 이 아닐경우에는 해당 객체를 리턴하고 null 이라면 괄호 안의 예외를 던진다.

 

 

✅ 참고) @Builder

Lombok에서 지원

Builder를 사용하고자 하는 클래스 위에 @Builder 애너테이션을 추가한다.

객체를 생성할 때 빌더를 함수를 통해 호출하고 셋팅하고자 하는 필드값을 하나씩 지정해주고 마지막에 build()로 닫아서 작동시킨다.

생성자 파라미터가 많을 경우 builder를 사용하면 가독성이 좋아진다.

자기 자신을 리턴하기 때문에 체인형식으로 생성해줄 수 있다.

최종적으로 build()를 해야 만들어진다.

Member.builder()
	.name("김아무개")
   	.city("서울")
   	.email("kim123@gmail.com")
   	.phone("010-1111-2222")
   	.build();

 

 

https://projectlombok.org/features/Builder

 

@Builder

 

projectlombok.org

 

 

 

 

읽어주셔서 감사합니다. ^^ 좋은하루 보내세요.

오개념에 대한 지적은 늘 환영입니다.

 

 

 

 

 

 

 

❑ Spring Data JDBC

 

 

Spring Data JDBC는 나온지 얼마안되서 아직은 Spring Data JPA를 현업에서 사용한다.

 

 

➤ DDD란?

 

도메인 주도 설계(Domain Driven Design)

모든 기능을 도메인 모델 위주로 돌아가는 설계 기법이다.

 

 

▶️ 도메인(Domain)이란?

 

도메인이란 엔티티의 속성들이 가질 수 있는 값들의 집합이다.

쉽게 생각하면 비즈니스적인 어떤 업무 영역이다.

도메인 지식(Domain knowledge)들을 서비스 계층에서 비즈니스 로직으로 구현해야 한다.

현실세계에서 접하는 업무의 한 영역이다.

 

 

도메인은 두가지 모델이 있다.

 

 

▶️ 빈약한 도메인 모델

 

편의상 완벽한 DDD를 완벽하게 적용하지 못하는 것을 빈약한 도메인 모델이라 부른다.

도메인 객체들에 비즈니스 로직이 거의 없거나 아예 없는 모델이다.

Service 클래스에 기능 집중 + 기능이 없는 빈약한 도메인 모델(Domain Entity Classs)

 

 

▶️ 풍부한(rich) 도메인 모델

 

서비스 클래스의 기능 축소 + 기능이 많은 풍부한 도메인 모델(rich Domain)

➡️ DDD

 

 

 

 

➤ Aggregate(애그리거트)란?

 

비슷한 업무 도메인들(하위 수준 도메인)의 묶음

비슷한 범주의 연관된 업무들을 하나로 그룹화 해놓은 그룹이다.

 

 

➤ Aggregate Root(애그리거트 루트)란?

 

aggregate안에는 1개 이상의 도메인이 있고

각각의 aggregate를 대표하는 도메인이 존재한다.

DDD에서 하나의 애그리거트를 대표하는 도메인을 애그리거트 루트(Aggregate Root) 라고 한다.

 

▶️ Aggregate Root 선정 기준

  • 다른 모든 도메인들과 직간접적으로 연관되어 있는 도메인
  • 데이터베이스의 테이블 간 관계에서 aggregate root 테이블은 부모 테이블이 되고 다른 도메인들은 자식 테이블이 된다.

 

 

➤ Aggregate 간의 관계

 

  • 1:N (1 대 다)
  • N:N (다 대 다)

 

 

애플리케이션에서 Domain Entity 클래스 간의 관계를 먼저 설계한다.

➡️ 도메인의 Aggregate Root를 찾는다.

 

ORM(Object-Relational Mapping)기반의 데이터 액세스 기술인 Spring Data JDBC에서는 엔티티 클래스 간의 관계를 정의하는 것이 필요하다.

ORM은 객체와 테이블을 매핑하는 기술이기 때문에 클래스 간의 연관 관계를 찾아야 한다.

데이터베이스 테이블 간의 관계는 외래키를 통해 맺어진다.

클래스끼리 관계는 객체의 참조를 통해 맺어진다.

 

 

 

 

 

도메인 엔티티 클래스 정의

 

 

➤ 애그리거트 객체 매핑 규칙

 

1. 모든 엔티티 객체의 상태는 aggrigate root를 통해서만 변경할 수 있다.

  • 직접 엔티티 객체의 상태를 변경하지 않고 애그리거트 루트를 거쳐서만 엔티티 상태를 변경해야 한다.

 

2. 하나의 동일한 aggrigate 내에서의 엔티티 객체 참조

  • 동일한 하나의 애그리거트 내에서는 엔티티 간에 객체로 참조된다.

 

3. 애그리거트 루트 대 애그리거트 루트 간의 엔티티 객체 참조

  • 애그리거트 루트 간의 참조는 객체 참조 대신 ID로 참조한다.
  • 1대1과 1대N 관계일 때는 테이블 간의 외래키 방식과 동일하다.
  • N대N 관계일 때는 외래키 방식과 객체 참조 방식이 함께 사용된다. 

 

 

 

➤ Entity 구현

 

 

▶️ @Entity

해당 클래스가 엔티티 클래스임을 알려준다.

해당 클래스는 데이터베이스 테이블과 매핑된다.

클래스명 위에 작성한다.

 

▶️ @Id

해당 멤버변수가 ID값 임을 알려준다.

애그리거트 간의 매핑에서 루트 간에는 객체로 직접 참조하는 것이 아니라 ID로 참조한다.

 

 

▶️ @Table(“”)

이 애너테이션을 추가하지 않으면 기본적으로 클래스명과 같은 테이블 이름과 매핑된다.

하지만 클래스명과 다른 테이블명을 가진 테이블과 매핑하려면 이 애너테이션을 사용하고 attribute로 이름을 지정해주어야 한다.

 

▶️ AggregateReference<T, ID>

1:N 관계에서 애그리거트 루트와 애그리거트 루트 간에 객체로 참조할 때 사용하는 객체이다.

ID로 참조한다.

직접적으로 객체를 참조하지 않고 AggregateRefence로 객체를 한 번 감싸준다.

 

 

▶️ N:N 관계에서의 매핑

 

N:N관계일 경우 직접 매핑할 수 없기 때문에 1:N, N:1 관계로 풀어줄 엔티티가 중간에 하나 필요하다.

데이터베이스에서 join table 해주는 것과 같다.

중간에서 매핑해줄 엔티티 클래스를 하나 작성해서 매핑해준다.

 

▶️ @MappingCollection(idColumn = “”)

엔티티 클래스 간에 연관 관계를 맺어주는 정보를 의미한다.

idColumn attribute 값으로 외래키에 해당하는 column명을 적어준다.

N:N 관계에서는 AggrementReference로 기본키를 감싸줄 필요 없다.

 

 

 

 

 

읽어주셔서 감사합니다. 좋은 하루 되세요

오개념에 대한 지적은 환영입니다. 😄

 

 

1 2 3 4 5 6