Skip to content

Commit

Permalink
Merge pull request #48 from SSUMC-6th/bey/#41
Browse files Browse the repository at this point in the history
[베이] Chapter 8. API 응답 통일 & 에러 핸들러
  • Loading branch information
h-ye-ryoung authored Jun 4, 2024
2 parents 4d18e68 + 0e9b69d commit e4561a1
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 0 deletions.
304 changes: 304 additions & 0 deletions docs/chapter8/ch08keyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
# keyword 1 : spring의 의존성 주입

### 의존성 주입 (DI)

---

> 어떤 객체가 사용하는 의존 객체를 `new` 연산자로 직접 만들어 사용하는 것이 아니라,
> 주입 받아서 사용하는 방법
> ex) 장난감들은 배터리에 의존하고 있다. 의존성 주입 → 배터리를 넣어주는 것
- 의존성 예시

```java
import org.springframework.stereotype.Service;

@Service
public class BookService {

private BookRepository bookRepository;

public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
```

```java
@Service
public class BookRepository {
// DI Test
}
```

- **BookService 클래스는 BookRepository 클래스의 의존성을 가진다.**

- 강한 결합: 객체 내부에서 다른 객체를 직접 생성
- 느슨한 결합: 외부에서 생성된 객체를 인터페이스를 통해 넘겨받는 것
- 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기 때문에 유연하다.

### Spring의 의존성

---

- 의존을 주입하는 코드를 개발자가 직접 구현하면 객체간 결합도가 증가할 수 있다.

스프링 프레임워크 존재 이유는 여기에 있다.

- Spring은 `컨테이너`라는 객체를 저장하는 공간이 있다.

컨테이너에 저장된 객체를 `Bean`이라고 부른다.

많은 객체가 의존하는 객체는 Bean으로 등록하고 필요할 때마다 컨테이너에서 빼온다.

- 컨테이너에 등록하는 방법

: Bean으로 만들고 싶은 객체를 어노테이션으로 표시한다. (`@Component`, `@Bean` ) 

스프링 프레임워크는 어노테이션을 스캔하여 Bean으로 등록한다.


![Untitled](image.png)

컨테이너에 Bean이 등록되면 Bean에 의존하는 객체에 주입해야 한다.

이때 주입하는 방법은 아래 3가지가 있다.

### 의존성 주입 방식

---

1. 필드 주입 (`@Autowired`)
- 클래스에 선언된 필드에 생성된 객체를 주입
- 주입할 필드 위에 @Autowired 어노테이션 사용

```java
@Controller
public class PetController{
@Autowired
private PetService petService;
}
```

2. 수정자 주입
- 클래스의 수정자를 통해 의존성 주입

```java
@Controller
public class PetController{

private PetService petService;

@Autowired
public void setPetService(PetService petService){
this.petService = petService;
}
}
```

3. 생성자 주입
- 클래스의 생성자를 통해 의존성 주입
- 생성자 주입은 인스턴스가 생성될 때 1회 호출되는 것이 보장
- 생성자 주입시 필드에 final 키워드를 사용할 수 있음

```java
@Controller
public class PetController{

private final PetService petService;

@Autowired
public PetController(PetService petService){
this.petService = petService;
}
}
```


- 클래스 주입시에는 아래의 두 특징과 `Lombok` 라이브러리를 통해 더 간편하게 작성

✅ 클래스 내 생성자가 한개

✅ 주입받을 객체가 Bean으로 등록

```java
@Controller
@RequiredArgsConstructor
public class PetController{
private final PetService petService;
}
```

- `@RequiredArgsConstructor` 로 필드를 포함한 생성자를 포함시켜주고 `@Autowired` 키워드를 생략해서 더 가독성이 좋은 코드로 사용 가능

### 생성자 주입 방식의 장점

---

1. `객체의 불변성` → 생성자를 통해 최초 1회만 주입하므로 불변 객체를 보장
2. 순환 참조 문제 방지
3. 테스트 용이


# keyword 2 : IoC 컨테이너

### IoC (Inversion of Control) : 제어의 역전

---

> 제어가 역전된 상태:
> 라이브러리를 사용할때는 개발자가 라이브러리를 호출해서 사용하지만, 프레임워크에서는 요청을 받았을 때 개발자가 개발해놓은 코드를 프레임워크가 호출하여 사용한다.
>
> 이때 `개발자 -> 코드(라이브러리)`에서 `코드(프레임워크) -> 개발자`로 호출의 주체가 바뀌게 되고 이것을 제어의 역전이라고 한다.

### 자바와 스프링의 실행 순서

---

```
자바
1. 객체 생성
2. 의존성 객체 생성 (클래스 내부에서 생성)
3. 의존성 객체 메소드 호출

스프링
1. 객체 생성
2. 의존성 객체 주입 (스스로 만드는 것이 아니라, 제어권을 스프링에게 위임하여 스프링이 만들어 놓은 객체를 주입)
3. 의존성 객체 메소드 호출
```
따라서 스프링이 모든 의존성 객체를 스프링이 실행될 때 만들어주고 필요한 곳에 주입시켜줌으로써 `Bean`들은 싱글톤 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤하는 것이 아니라 스프링에게 맡겨 작업을 처리하게 된다.
### IoC 컨테이너의 종류
---
1. `BeanFactory`
- IoC 컨테이너의 기본이 되는 인터페이스
- Bean을 관리하는 역할
2. `ApplicationContext`
- BeanFactory를 상속받고 있기 때문에 BeanFactory와 같은 기능을 함
- 또한 이외에도 다양한 클래스들을 상속받아 더 많은 기능을 제공
- 텍스트 메시지 관리, 이벤트 발생 알림 등
# keyword 3 : RestControllerAdvice
< 애플리케이션 전역에서 발생하는 예외를 한 곳에서 관리 >
### @ControllerAdvice
---
- `@ExceptionHandler`, `@ModelAttribute`, `@InitBinder` 가 적용된 메서드들에
AOP를 적용해 Controller 단에 적용하기 위해 고안된 어노테이션
- 모든 `@Controller`에 대한, 전역적으로 발생할 수 있는 예외를 잡아서 처리할 수 있다
### @RestControllerAdvice
---
- `@ControllerAdvice`와 `@ResponseBody`를 합쳐놓은 어노테이션
- `@ControllerAdvice`와 동일한 역할을 수행하고,
추가적으로 `@ResponseBody`를 통해 객체를 리턴할 수도 있dma
- 따라서 단순히 예외만 처리하고 싶다면 → `@ControllerAdvice`
응답으로 객체를 리턴해야 한다면 → `@RestControllerAdvice`를 적용
### 사용 예제
---
```java
@RestControllerAdvice
public class EditAdviser {
@ExceptionHandler(Exception.class)
public void exceptionHandler(Exception e) {
System.out.println(e.getMessgae());
}
}
```

- 위와 같이 어노테이션을 선언하면, 알아서 해당 예외를 `exceptionHandler()` 메서드에 정의된 로직으로 처리할 수 있음
- ExceptionHandler 함수는 `Exception.class`와 그 하위 클래스 예외를 특정적으로 처리

- 아래와 같이 Controller에서 예외처리를 따로 하지 않아도 됨

```java
@GetMapping("/test")
public ResponseEntity<String> exceptionHandlerTest() throws Exception {
return new ResponseEntity(test(), HttpStatus.OK);
}
```

# keyword 4 : lombok

### Lombok

---

- 어노테이션 기반으로 코드 자동완성 기능을 제공하는 라이브러리
- EX) Getter, Setter, ToString, Constructor(생성자)

- Lombok 장점
1. 어노테이션을 통한 코드 생산성, 편의성 증가
2. Code의 길이가 줄어들어 가독성, 유지보수성 향상
3. Builder 패턴의 적용, Log 생성 등 편의성

### Lombok 기능

---

1. `@Getter` / `@Setter`

<aside>
💡 클래스 이름 위에 적용시키면 모든 변수들에 적용 가능
변수 이름 위에 적용시키면 해당 변수들만 적용 가능

</aside>


2. `@AllArgsConstrucr`
- 모든 변수를 사용하는 생성자를 자동완성시켜주는 어노테이션

3. `@NoArgsConstructor`
- 어떤한 변수도 사용하지 않는 기본 생성자를 자동완성 시켜주는 어노테이션

4. `@RequiredArgsConstructor`
- 특정 변수만을 활용하는 생성자를 자동완성 시켜주는 어노테이션
- 생성자의 인자로 추가할 변수에 `@NonNull` 어노테이션을 붙여서

해당 변수를 생성자의 인자로 추가할 수 있다.

- 아니면 해당 변수를 final로 선언해도 의존성을 주입받을 수 있다.
- 예제

```java
@Getter
@RequiredArgsConstructor
public class Store extends Common {

@NonNull
private String companyName; // 상호명
private final String industryTypeCode; // 업종코드
private String businessCodeName; // 업태명
private String industryName; // 업종명(종목명)

/* RequiredArgsConstructor 통해 아래의 생성자를 자동 생성할 수 있다.
public Store(String companyName, String industryTypeCode) {
this.companyName = companyName;
this.industryTypeCode = industryTypeCode;
}
*/

}
```


5. `@Builder`
- 해당 클래스의 객체의 생성에 Builder패턴을 적용시켜준다.
Binary file added docs/chapter8/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/mission/chapter8/ch08mission_01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
깃허브 링크:
https://github.com/h-ye-ryoung/umc-practice/tree/mission8

과정 인증샷:

1. 코드 작성중 (1)
![Untitled](images/1.png)

2. 코드 작성중 (2)
![Untitled](images/2.png)

3. localhost test
![Untitled](images/3.png)
![Untitled](images/4.png)
31 changes: 31 additions & 0 deletions src/mission/chapter8/ch08mission_02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# RestControllerAdvice의 장점

### 장점

---

1. 모든 컨트롤러에서 발생하는 예외를 한 곳에서 처리
- 코드 중복을 줄이고 일관성 있는 예외처리가 가능
2. 코드 중복 감소
- 컨트롤러마다 예외처리를 반복해서 작성할 필요 X
- 한 클래스에서 예외를 처리함으로써 중복을 줄임
3. 유지보수 용이
- 예외 처리 로직이 한 곳에 집중되어 유지보수가 용이
- 여러 컨트롤러를 수정할 필요 X
4. 응답 형식의 일관성
- **일관된 형식으로 에러 응답을 반환할 수 있음**
- JSON 형식으로 통일된 에러 응답을 제공

→ 클라이언트에서 예외를 쉽게 처리할 수 있게 됨

5. 다양한 예외에 대해 구체적인 예외 처리 가능
- 각 예외 타입별로 메소드를 분리해 구체적으로 예외처리 가능

### RestController가 없다면?

---

1. 중복 코드 증가
2. 유지보수 어려움
3. 응답 형식이 일관적이지 X
4. 복잡한 코드
Binary file added src/mission/chapter8/images/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mission/chapter8/images/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mission/chapter8/images/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mission/chapter8/images/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e4561a1

Please sign in to comment.