Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions Database/인덱스를 사용하는 이유(장단점).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# 인덱스를 사용하는 이유(장단점)
## 요약
> 인덱스는 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조입니다.
> 사용 시 테이블 조회 속도를 높여 DB 성능을 향상시키며, Full Scan하지 않아 시스템 부하를 줄여줍니다.
> 하지만 항상 정렬된 상태로 유지해야하기에 추가적인 쓰기작업이 필요하며 이를 저장하기 위한 저장공간이 필요합니다. 때문에 잘못 사용할 경우 성능 저하의 역효과를 발생시킵니다.


## 인덱스(Index)란?
- 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조.
- 추가적인 쓰기 작업과 저장공간을 활용
-> 이를 관리하기 위해 DB에 ~~약 10%에 해당하는~~ 저장공간 필요
- **PK는 자동으로 Index가 생성됨** -> NHN 기출
![인덱스 구조](https://github.com/leeejuhyeong/images/blob/main/no-study-no-future/Database/index_structure.png?raw=true)

> 인덱스 없이 조회하면 모든 튜플(레코드)를 검색하지만 인덱스를 사용한다면 인덱스를 탐색하고 일치할 시에 해당 튜플로 데이터를 검색. -> 속도 **up**


## 인덱스를 사용하는 이유
- 인덱스 테이블은 데이터들이 정렬되어 있음! -> **정렬과 연관지어 기억하자**

### 조건 검색 Where 절의 효율성
- 인덱스 테이블은 데이터들이 정렬되어 저장되어 있기 때문에 해당 조건(where)에 맞는 데이터를 빠르게 찾아낼 수 있음

### 정렬 Order by 절의 효율성
- 이미 정렬되어 있기 떄문에 order by에 의한 Sort과정을 피할 수 있음

### MIN, MAX의 효율적인 처리 가능
- MIN값과 MAX값을 레코드의 시작값과 끝 값 한 건씩만 가져오면 되기에 Full Scan보다 효율적


## 인덱스의 장단점
### 장점
- 테이블을 조회하는 속도와 그에 따른 성능을 향상
- 전반적인 시스템의 부하를 줄임(Full Scan을 안해서)

### 단점
- 인덱스를 관리하기 위해 DB의 ~~약 10%에 해당하는~~ 추가적인 저장공간 필요
- 인덱스를 관리하기 위해 추가 작업 필요
- 잘못 사용할 경우 성능 저하의 역효과 발생


## 인덱스 관리
- Index를 항상 **최신의 데이터를 정렬된 상태로 유지**해야 원하는 값을 탐색 할 수 있음
- INSERT, UPDATE, DELETE(데이터 추가, 수정, 삭제) 수행 시 정렬 필요! -> 부하**UP**
- INSERT, UPDATE, DELETE 시 추가적인 연산 필요
-> 부하를 최소화하기 위해 데이터 삭제라는 개념에서 **인덱스를 사용하지 않는다** 라는 작업으로 대신함
- INSERT : 새로운 데이터에 대한 인덱스 추가
- DELETE : 삭제하는 데이터의 인덱스를 사용하지 않는다는 작업 진행
- UPDATE : 기존의 인덱스를 사용하지 않음 처리, 갱신된 데이터에 대한 인덱스 추가


## 인덱스 생성 전략
- 조건절에 자주 등장하는 컬럼(호출 빈도가 높음)
- 같거나 크고, 작음으로 자주 사용되는 컬럼(where 또는 order by에서 쓰이는 컬럼)
- 중복되는 데이터가 최소한인 컬럼(분포도가 좋다 = 데이터가 서로 다르고 넓게 퍼져 있다)
- 조인 조건으로 자주 사용되는 컬럼


## 인덱스 자료구조
### 해시테이블
- key : 데이터(= 컬럼의 값)
- value : 데이터의 위치
- 장점
- 빠른 데이터 탐색에 유용
- 시간 복잡도 O(1)
- 단점
- 등호 연산에만 특화
- DB는 부등호(>, <) 연산이 많아서 **사용 부적합**
![인덱싱_해쉬](https://github.com/leeejuhyeong/images/blob/main/no-study-no-future/Database/indexing_hash.png?raw=true)

## B+ Tree
- 대부분의 DB 인덱싱 자료구조
- 특징
- 리프노드에만 인덱스와 함께 데이터를 갖고있고, 나머지 노드들은 인덱스만을 가짐
- 리프노드들은 연결리스트로 이루어짐 -> 탐색이 0번노드부터 일어나는 연결리스트에 비해 시간 효율이 좋음!(log2(N))
- 데이터 노드의 크기는 인덱스 노드와의 크기와 같지 않아도 됨
- 예시
- 인덱스
![인덱싱_B+Tree1](https://github.com/leeejuhyeong/images/blob/main/no-study-no-future/Database/indexing_b%2Btree1.jpeg?raw=true)

- B+ Tree
![인덱싱_B+Tree2](https://github.com/leeejuhyeong/images/blob/main/no-study-no-future/Database/indexing_b%2Btree2.jpeg?raw=true)

> BTree : 모든 노드에 데이터를 담음
> 리프노드 : 자식이 없는 노드. 잎이라고도 함
> B+ Tree 알고리즘에 대해서는 추후 정리할 예정입니다.



## TMI : 결합 인덱스
- 두 개 이상의 컬럼을 합쳐서 인덱스를 만드는 것
- 단일 컬럼으로는 분포도가 나쁘지만, 여러 개의 컬럼을 합친다면 좋은 분포도를 가지며, where절에서 and 조건에 많이 사용되는 컬럼을 결합 인덱스로 생성함

### 결합 인덱스 컬럼 생성 전략
- where절에서 and 조건으로 자주 결합되고, 결합할 시 분포도가 좋아지는 컬럼
- 다른 테이블과 조인의 연결고리로 자주 사용되는 컬럼
- order by에서 자주 사용되는 컬럼
- 하나 이상의 키 컬럼 조건으로 같은 테이블의 컬럼들이 자주 조회될 때

### 예시
- 결합 인덱스 생성 예시
`create index emp_pay_idx on emp_pay(급여년월, 급여코드, 사원번호);`
- 결합 인덱스 사용 예시
```
select * from emp_pay where 급여년월 = '202107';
select * from emp_pay where 급여년월 = '202107' and 급여코드 = '정기급여';
select * from emp_pay where 급여년월 = '202107' and 급여코드 = '정기급여' and 사원번호 = '20210401';
```

> 단 급여코드만을 검색할 때는 결합 인덱스의 첫 번째 컬럼인 급여년월의 조건식이 없기 때문에 결합 인덱스로 조회하지 않음! -> 급여년월, 급여코드, 사원번호의 순서대로 조회
- 결합 인덱스 효율성 감소
1. LIKE 검색 : 2021%같이 검색할 경우 2021에 해당하는 모든 급여코드, 정기급여로 생성된 결합 인덱스를 조회해서 B*Tree에서 조회하기 어려움
2. 결합 인덱스 칼럼 순서(이하 급여년월(1), 급여코드(2), 사원번호(3) 컬럼을 숫자로 말하겠습니다)
- 1, 3 / 2 / 2, 3 / 3 으로 조회할 경우 결합 인덱스 순서대로 조회하지 않아서 Full Scan이 발생합니다


- 출처
- [Database 인덱스(index)란? - MangKyu’s Diary](https://mangkyu.tistory.com/96)
- [DB 데이터베이스 인덱스(Index) 란 무엇인가?](https://coding-factory.tistory.com/746)
- [자료구조 그림으로 알아보는 B+Tree](https://velog.io/@emplam27/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-B-Plus-Tree)
86 changes: 86 additions & 0 deletions Java/Exception 종류와 처리 방법.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Exception 종류와 처리 방법

## 요약
- Checked Exception은 컴파일 오류로 실행 시점에서 발생하며, Unchecked Exception은 런타임 오류로 개발자의 실수로 발생하는 경우가 많다
- Checked Exception은 오류 시 트랜잭션 롤백이 발생하지 않고 Unchecked Exception은 롤백이 발생한다

## Exception 종류
![Exception 종류](https://github.com/leeejuhyeong/images/blob/main/no-study-no-future/Java/Exception%20%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%85%E1%85%B2.jpeg?raw=true)

- Error
- Exception
- Checked Exception
- Unchecked Exception


## Error
- java.lang.Error 클래스의 서브 클래스
- JVM에서 발생하며 메모리 부족 등과 같은 **시스템이 비정상적인 상황**인 경우에 사용
- 애플리케이션 코드에서 잡아서는 안되며, 잡아서 대응할 수 있는 방법도 없음.


## Exception
- java.lang.Exception 클래스의 서브 클래스
- 애플리케이션 코드에서 예외가 발생했을 경우 사용
- Checked Exception, Unchecked Exception가 있다

### Checked Exception
- RuntimeException 클래스를 상속하지 않은 Exception 클래스
- 예외를 처리하지 않으면 컴파일 에러가 발생
- 따라서 예외를 처리하기 위해 try-catch 또는 throws를 통해 처리
> FileNotFoundException, ClassNotFoundException, IOException, SqlException 등

### Unchecked Exception
- RuntimeException 클래스를 상속한 Exception 클래스
- 예외를 처리하지 않아도 컴파일 에러가 발생 ❌
- 발생하는 예외는 RuntimeException을 상속했기에 동일

### Runtime Exception이란
- 실행 중에 발생하는 예외로, 시스템 환경적으로나 input값이 잘못된 경우, 혹은 의도적으로 프로그래머가 잡아내기 위한 조건 등에 부합될 때 발생
> ArrayIndexOutofBoundsException, NullPointerException, IllegalArgumentException 등

### 구분 기준(차이)
- 예외를 확인할 수 있는 시점 구분
- Checked Exception은 컴파일 단계에서 명확하게 체크가 가능한 것들, 실행 ❌
- Unchecked Exception은 실행 과정 중 어떠한 특정 논리에 의해 발견

### 왜 알아야할까?
- 꼭 처리를 해야하는 기준이 된다
- Checked Exception의 경우 예외를 처리하지 않으면 컴파일 에러가 발생하기 때문에 반드시 예외 처리가 필요
- Unchecked Exception은 개발자의 부주의로 발생하는 경우가 대부분이며, 미리 예측하지 못했던 상황에서 발생하는 예외가 아니기 때문에 굳이 로직으로 처리할 필요가 없도록 만들어져 있다
- 예외 발생 시 트랜잭션의 Roll-Back 여부
- Checked Exception의 경우 예외 발생 시 트랜잭션이 Roll-Back하지 않는다
- Unchecked Exception의 경우 예외 발생 시 트랜잭션이 Roll-Back한다
- Spring Framework가 제공하는 선언적 트랜잭션(@Transactional)안에서 에러 발생 시 Checked Exception은 롤백이 되지 않고, Unchecked Exception은 롤백이 됨(물론 옵션을 변경할 수 있음)


## 예외 처리 방법
### 예외 복구
- 문제를 해결해서 정상 상태로 돌려놓는 것

### 예외 처리 회피
- 예외 처리를 직접 처리하지 않고, 자신을 호출한 곳으로 던져버리는 것
- 무작정 예외를 넘기는 것은 무책임한 회피가 될 수 있으므로 상황에 따라 적절한 사용이 필요

### 예외 전환
- 목적
- 의미 있고 추상화된 예외로 바꾸는 경우
- 중복 아이디 체크 : 동일한 아이디가 존재하면 SqlException이 발생하지만 이를 DuplicatedUserIdException과 같은 예외로 변환하면서 서비스 계층에서 왜 예외가 발생했는지 확실한 의미를 전달
- 런타임 예외로 포장하여 불필요한 처리를 줄요주는 경우
- Checked Exception으로 의해 불필요한 에러 처리가 많다면 이를 Unchecked Exception으로 변경하여 불필요한 처리를 줄임
- 복구 못할 예외라면 불필요하게 체크를 할 필요가 없기 때문


## 올바른 예외 처리 방법
- 조치가 없는 Try-Catch → 상황에 맞는 조치 진행
- Try-Catch로 예외를 잡을 경우 반드시 조치를 해주자
- 무분별한 throws Exception → Unchecked Exception으로 전환
- 무책임한 throws Exception은 어떤 문제가 발생했는지(의미 있는 정보)를 얻을 수 없기 때문
- SqlException과 같은 복구가 불가능한 예외라면 가능한 빨리 Unchecked/Runtime 예외로 전환해주는 것이 좋음


## 출처
- [Java 예외(Exception) 종류와 올바른 예외 처리 방법 - MangKyu’s Diary](https://mangkyu.tistory.com/152)
- [Java Checked Exception vs Unchecked Exception 정리 :: Gyun’s 개발일지](https://devlog-wjdrbs96.tistory.com/351)
- [RuntimeException란 무엇인가?](https://nhj12311.tistory.com/204)
- [Java 예외(Exception) 처리에 대한 작은 생각](https://www.nextree.co.kr/p3239/)
Binary file added Spring/.DS_Store
Binary file not shown.
153 changes: 153 additions & 0 deletions Spring/생성자 주입을 사용해야하는 이유.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# 생성자 주입을 사용해야하는 이유
# 요약
- [객체의 불변성 확보](bear://x-callback-url/open-note?id=B5467B00-E1D6-4C5B-8FA4-83B1A3B2975E-1500-00000B9786F1BC1D&header=1.%20%EA%B0%9D%EC%B2%B4%EC%9D%98%20%EB%B6%88%EB%B3%80%EC%84%B1%20%ED%99%95%EB%B3%B4)
- [테스트 코드 작성 용이](bear://x-callback-url/open-note?id=B5467B00-E1D6-4C5B-8FA4-83B1A3B2975E-1500-00000B9786F1BC1D&header=2.%20%ED%85%8C%EC%8A%A4%ED%8A%B8%20%EC%BD%94%EB%93%9C%EC%9D%98%20%EC%9E%91%EC%84%B1)
- [final키워드 작성으로 실수를 줄이고 Lombok과의 연계가 좋다](bear://x-callback-url/open-note?id=B5467B00-E1D6-4C5B-8FA4-83B1A3B2975E-1500-00000B9786F1BC1D&header=3.%20final%20%ED%82%A4%EC%9B%8C%EB%93%9C%20%EC%9E%91%EC%84%B1%20%EB%B0%8F%20Lombok%EA%B3%BC%EC%9D%98%20%EA%B2%B0%ED%95%A9)
- [순환 참조 에러를 사전에 방지한다](bear://x-callback-url/open-note?id=B5467B00-E1D6-4C5B-8FA4-83B1A3B2975E-1500-00000B9786F1BC1D&header=4.%20%EC%88%9C%ED%99%98%20%EC%B0%B8%EC%A1%B0%20%EC%97%90%EB%9F%AC%20%EB%B0%A9%EC%A7%80)


# 1. 객체의 불변성 확보
- 대부분 의존 관계 주입은 애플리케이션 종료시점까지 변하면 안됨!
- Setter를 열어두는 것은 좋은 설계 방법이 아님(누군가 실수로 변경할 수 있음)
-> OCP(Open/closed principle) 위배
- 생성자 주입은 생성 시 딱 1번만 호출됨으로 불변하게 설계할 수 있음
- final 키워드 설정 가능(불변임으로)


# 2. 테스트 코드의 작성
## 생성자 주입이 아닐 경우
- 필드 주입을 사용한다면 테스트 환경은 실제 코드의 DI 프레임워크 위에서 동작하지 않고, 순수한 자바 코드로 동작 -> **Null Point Exception 에러 발생, 테스트 하는 것이 불가능**

### 실제 코드
```
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

public UserRepository getUserRepository() {
return userRepository;
}
}
```

### 테스트 코드
```
public class UserServiceTest {
@Test
public void injectionTest() {
UserService userSerivce = new UserServiceImpl();
UserRepository ur = userService.getUserRepository();
System.out.println(ur);
}
```

> DI 프레임워크가 없는 순수한 자바 코드 테스트임으로 의존 관계 주입이 누락되어 UserRepository가 null인 NPE발생

- 위의 문제를 해결하기 위해선 Setter를 사용하면 되지만**, 싱글톤 패턴을 해칠 수 있다.** 또한 수정자 주입과 다를 바가 없으면서 수정자 주입 역시 **OCP 위배**한다는 문제가 있음

> OCP(Open/closed principle) : 개방-폐쇄 원칙, 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.


## 생성자 주입일 경우
- 의존 관계 주입이 누락될 경우 **컴파일 오류**가 발생
- 어떤 값을 필수로 주입해야하는 지 확실하게 알 수 있음!

### 실제 코드
```
@Service
public class UserServiceImpl implements UserService {

private UserRepository userRepository;

@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}

public UserRepository getUserRepository() {
return userRepository;
}
}
```

### 테스트 코드
```
public class UserServiceTest {
@Test
public void injectionTest() {
UserService userSerivce = new UserServiceImpl();
UserRepository ur = userService.getUserRepository();
System.out.println(ur);
}
```

> 컴파일 오류 발생 : `java: constructor UserServiceImpl in class (패키지명).UserServiceImpl cannot be applied to given types; required: (패키지명).UserRepository`


# 3. final 키워드 작성 및 Lombok과의 결합
## final 키워드 작성
- 생성자 주입은 final 키워드 사용 가능
-> 값 설정 누락 시 컴파일 시점에 오류 발생
- 누락된 의존성 확인이 빠름!

## Lombok과의 결합
- `@RequiredArgsConstructor`
-> final이 붙은 필드(변수)를 모두 모아 생성자를 자동으로 생성
- 필드 주입보다 더 간결한 코드!

> **최종 코드**
```
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;
}
```


# 4. 순환 참조 에러 방지
## 필드 or 수정자 주입의 경우
- 예시 (닭은 알을 낳고, 알은 닭으로 부화하고)
```
@Component
public class Chicken {
@Autowired Egg egg;

public void layEgg() {
egg.hatch();
}
}
```

```
@Component
public class Egg {
@Autowired Chicken chicken;

public void hatch() {
chicken.layEgg;
}
}
```

> 두 메소드는 서로를 순환하며 호출하다가, 메모리에 CallStack이 쌓여 StackOverFlow 에러를 발생

## 생성자 주입의 경우
- 애플리케이션 구동 시점(객체 생성 시점)에 컴파일 오류를 발생시켜 순환 참조 에러를 방지

```
Description: The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| chicken defined in file [(경로)/Chicken.class]
↑ ↓
| egg defined in file [(경로)\Egg.class]
└─────┘
```

> 생성자 주입의 컴파일 에러가 오류 잡기 좋다..

## 출처
- [스프링 핵심 원리 - 기본편 - 인프런 | 강의](https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard)
Loading