Til (60)

 

 

 

 

 

 

 

JDBC 기반 데이터 액세스 계층

 

 

JDBC란?

 

 

JDBC(Java Database Connectivity)

Java 기반 애플리케이션의 코드 레벨에서 사용하는 데이터를

데이터베이스에 저장, 업데이트 하거나

데이터베이스에 저장된 데이터를 Java 코드 레벨에서 사용할 수 있게

해주는 Java에서 제공하는 표준 API이다.

 

 

JDK 1.1 버전부터 제공됐다. 엄청 오래됐다..!

데이터베이스 액세스를 위해 Java에서 제공하는 API라고 보면 된다.

다양한 벤더(Oracle, MS SQL, MySQL 등)의 데이터베이스와 연동할 수 있다.

 

etc..

  • Java 기반의 애플리케이션에서 사용하는 데이터 액세스 기술의 기본이 되는 저수준(low level) API 이다.
  • Spring에서는 JDBC API를 직접 사용하기 보다는 Spring Data JDBC, Spring Data JPA 같은 기술이 편리해서 더 많이 사용한다. (Spring Data JDBC나 Spring Data JPA가 더 편리해서 현업에서는 JDBC를 많이 쓰지 않지만 예전 기술로 만든 애플리케이션을 그대로 사용할 경우 Spring JDBC를 사용할 수도 있다.)
  • Spring Data JDBC나 Spring Data JPA 같은 기술도 내부적으로 JDBC를 이용하기 때문에 JDBC의 흐름정도는 알면 데이터 액세스 기술을 구현하는데 도움이 된다.

 

 

 

 

➤ JDBC의 동작 흐름

 

JDBC의 동작 흐름

 

 

 

▶️ JDBC 드라이버(JDBC Driver)

 

JDBC 드라이버는 Database와의 통신을 담당하는 인터페이스이다.

Oracle, MS SQL, MySQL 등에서 해당 벤더에 맞는 JDBC 드라이버를 구현해서 제공을 한다.

JDBC 드라이버 구현체를 이용해서 특정 벤더의 Database에 액세스 할 수 있다.

 

 

 

➤ JDBC API 사용 흐름

 

JDBC API 사용 흐름

 

 

1. JDBC 드라이버 로딩

사용하고자 하는 JDBC 드라이버를 로딩한다.

JDBC 드라이버는 DriverManager라는 클래스를 통해서 로딩된다.

 

2. Connection 객체 생성

JDBC 드라이버가 정상적으로 로딩되면 DriverManager를 통해 데이터베이스와 연결되는 세션(Session)인 Connection 객체를 생성한다.

 

3. Statement 객체 생성

Statement 객체는 작성된 SQL 쿼리문을 실행하기 위한 객체이다.

객체 생성 후에 정적인 SQL 쿼리 문자열을 입력으로 가진다.

 

4. Query 실행

생성된 Statement 객체를 이용하여 입력한 SQL 쿼리를 실행한다.

 

5. ResultSet 객체로부터 데이터 조회

실행된 SQL 쿼리문에 대한 결과 데이터 Set으로 부터 데이터를 조회한다.

 

6. ResultSet 객체 Close, Statement 객체 Close, Connection 객체 Close

JDBC API 를 통해 사용된 객체들은 사용이 끝나면 실행한 역순으로 차례대로 Close 해준다.

 

 

 

➤ Connection Pool 이란?

 

Connection Pool

 

데이터베이스와의 연결을 위한 Connection 객체를 생성하는 작업은 비용이 많이 드는 작업이다.

왜 그런지는 아래 MySQL에서 제공하는 페이지를 들어가서 보면 알 수 있다.

 

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

 

MySQL :: MySQL 8.0 Reference Manual :: 8.2.5.1 Optimizing INSERT Statements

8.2.5.1 Optimizing INSERT Statements To optimize insert speed, combine many small operations into a single large operation. Ideally, you make a single connection, send the data for many new rows at once, and delay all index updates and consistency checkin

dev.mysql.com

 

 

효율을 위해서 Connection Pool에서 Connection을 미리 만들어서 보관한다.

그리고 애플리케이션이 필요할 때마다 이 Connection 객체를 제공하는 역할을 한다. (Connection 관리자)

애플리케이션 로딩 시점에 Connection 객체를 미리 생성해두고

애플리케이션에서 데이터베이스에 연결이 필요할 때마다

Connection 객체를 생성하지 않고(비용이 많이 듬)

미리 만들어둔 Connection 객체를 사용한다.

-> 애플리케이션의 성능 향상

 

Spring Boot 2.0 이전 버전에서는 Apache 재단의 오픈 소스인 Apache Commons DBCP(Database Connection Pool, DBCP)를 주로 사용했다.

2.0 이후 부터는 성능면에서 더 나은 HikariCP를 기본 DBCP로 채택했다.

 

※ 추가학습) HikariCP 란?

  • Brett Wooldridge가 2012년에 개발한 JDBC Connection Pool이다.
  • 가볍고 빠르고 안정적이다. (히카리는 일본어로 빛 광이다.)
  • Spring Boot 2.0부터 default DBCP로 채택되었다.

 

 


https://github.com/brettwooldridge/HikariCP

 

GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last.

光 HikariCP・A solid, high-performance, JDBC connection pool at last. - GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last.

github.com

 

 

 

※ 추가학습) JDBC 장단점

 

- 장점

  • 깨끗하고 심플한 SQL 처리가 가능하다
  • 용량이 큰 데이터에서 우수한 성능을 보인다
  • 구현하기 쉽다
  • 작은 애플리케이션에서 아주 좋은 성능을 보인다

 

- 단점

  • 대형 프로그래밍에서 많은 비용, 시간이 든다
  • OOP의 구현이 지원되지 않는다
  • JDBC를 이용한 MVC 패턴 구현이 어렵다
  • JDBC에서 사용되는 쿼리문은 database에 한정된다
  • 예외 처리를 위한 try, catch, finally 블록에 모든 것을 작성해야 한다.
  • 개발자가 connection을 닫지 못하면 JDBC가 자동으로 connection을 닫지 않아 문제가 발생할 수 있다.
  • 많은 곳에 SQL 쿼리문을 작성해야하고 일단 프로그램이 작성되면 테이블 구조를 수정할 수 없다.
  • JDBC는 데이터베이스에 특정 오류코드를 생산하고 개발자는 데이터베이스의 오류 코드에 대해 전혀 알지 못 할수 있다.
  • JDBC를 사용하면 Java 객체를 DB 객체로 변환하거나 그 반대로 변환하므로 변환 및 역변환에 많은 시간이 소요되어 프로세스가 느려질 수 있다.

 

출처 : 

https://techxyte.com/tutorials/hibernate/pros-and-cons-of-jdbc.php

 

Premium Bootstrap 5 HTML, Angular 11, VueJS, React & Laravel Admin Dashboard Themes

Metronic - #1 Selling Premium Bootstrap Admin Theme of All Time. Built with Twitter Bootstrap 5 HTML, Angular 11, VueJS, React and Laravel. Trusted By Tens of Thousands Users.

keenthemes.com

 

 

 

 

Spring Data JDBC 란?

 

 

➤ Data Access 

 

Spring 에서 데이터베이스에 액세스하는 다양한 기술을 사용할 수 있다.

대표적인 데이터 액세스 기술: mybatis, Spring JDBC, Spring Data JDBC, JPA, Spring Data JPA 

Spring에서 사용하는 데이터베이스 액세스 기술은 크게 두 가지로 나눌 수 있다.

 

 

➤ SQL 중심 기술

 

  • mybatis와 Spring JDBC가 대표적
  • 애플리케이션에서 데이터베이스에 접근하기 위해 SQL 쿼리문을 애플리케이션 내부에 Java 코드로 직접적으로 작성하는 것
  • 과거부터 현재까지 많이 사용하고 있지만 Java 진영에서는 SQL 중심보다 객체(Object) 중심의 기술로 지속적으로 이전하고 있는 추세이다.

 

 

mybatis 에서 사용하는 SQL Mapper 예시

<select id="findMember" resultType="Member">

  SELECT * FROM MEMBER WHERE member_id = #{memberId}

</select>

 

Spring JDBC의 JdbcTemplate 사용 예

Member member = this.jdbcTemplate.queryForObject(

"select * from member where member_id=?", 1, Member.class);

 

 

 

➤ 객체(Object) 중심 기술

 

  • 모든 데이터를 객체(Object) 관점으로 바라보는 기술이다.
  • SQL 쿼리문을 직접적으로 작성하기 보다 Java 객체를 이용해 애플리케이션 내부에서 Java 객체를 SQL 쿼리문으로 자동 변환해준다.
  • ORM(Object-Relational Mapping) 이라고 한다.
  • Java 에서 대표적 ORM 기술 : JPA(Java Persistence API), Spring Data JDBC

 

 

 

➤ Spring Data JDBC란?

 

  • 심플하다.
  • Spring Data JDBC는 ORM 기술을 사용하지만 JPA의 기술적 복잡도를 낮춘 기술이다.

 

 

▶️ Spring Data JDBC vs JPA vs Spring Data JPA

 

Spring Data JDBC 

  • 2018년에 처음 릴리즈 됐다.
  • 아직까지는 JPA보다 상대적으로 적게 사용된다.
  • 애플리케이션의 규모가 상대적으로 크지 않고, 복잡하지 않을 경우 Spring Data JDBC가 뛰어난 생산성을 보여준다.

 

JPA

  • 실무에서 가장 많이 사용하고 있는 기술이다.

 

Spring Data JPA

  • Spring Data JPA는 Spring에서 JPA 기술을 편리하게 사용하기 위한 기술이다.
  • JPA에 대한 선행 지식이 필요하다.

 

 

 

 

Spring Data JDBC 실습

 

 

➤ 의존 라이브러리 추가

 

implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'

runtimeOnly 'com.h2database:h2'

 

Spring Data JDBC를 사용하기 위한 의존 라이브러리 코드 추가

데이터베이스에서 인메모리(In-memory) DB인 H2를 사용하기 위한 의존 라이브러리 설정 추가

 

 

 

▶️ 인메모리(In-memory) DB란?

 

메모리 안에 데이터를 저장하는 데이터베이스이다.

메모리는 휘발성이라 컴퓨터 전원을 껐다가 켜면 메모리에 저장되어 있는 데이터는 모두 지워진다.

인메모리 DB는 애플리케이션이 실행되는 동안에만 데이터를 저장하고 있다.

테스트에 필요한 데이터 외에는 테이블에 없는 것이 테스트의 정확도 면에서 유리하기 때문에 인메모리 DB를 사용한다.

로컬 개발 환경에서는 테스트가 끝나면 테이블을 비우는 것이 좋다.

 

 

▶️ application.yml파일에 H2 Browser 활성화 설정 추가

 

Spring Boot Initializr을 통해 샘플 프로젝트 생성 -> src/main/resources 에 application.properties 또는 appication.yml 파일을 통해 Spring 에서 사용하는 다양한 설정 정보들을 입력

application.yml로 확장자 변경 후 아래 코드 입력

spring:

  h2:

    console:

      enabled: true

 

<애플리케이션 실행 시 콘솔 창>

HikariPool-1 - Starting...

HikariPool-1 - Start completed.

H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:97f0b3b1-369a-498d-9abe-633d5d10acaa'

 

<웹 브라우저>

localhost:8080/h2-console

주소창에 입력후 엔터

 

H2 Browser 초기 화면 나옴

JDBC URL 에 jdbc:h2:mem:97f0b3b1-369a-498d-9abe-633d5d10acaa 입력

-> Connect

 

 

 

▶️ H2 DB 디폴트 설정의 문제점

 

애플리케이션 재시작 할때마다 애플리케이션 로그에 출력되는 JDBC URL이 매번 랜덤하게 바뀌기 때문에 매번 변경된 JDBC URL을 다시 입력해야 한다.

이 문제는 application.yml 파일에 H2에 대한 설정을 추가해서 해결한다.

 

jdbc:h2:mem:test 로 설정

spring:

  h2:

    console:

      enabled: true

      path: /h2     # (1) Context path 변경

  datasource:

    url: jdbc:h2:mem:test     # (2) JDBC URL 변경

 

주소창에

localhost:8080/h2 로 접속

 

 

▶️ PostMan 으로 Post

 

Post 하고 body 에 JSON 형식으로 message 값 전달

➡️ 오류 발생! ➡️ Message 테이블이 생성되지 않았음

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

비즈니스 로직은 서비스 계층에 있다.

 

 

 

비즈니스 로직 예외 던지기(throw) 및 예외 처리

 

 

➤ 체크 예외(Checked Exception)와 언체크 예외(Unchecked Exception)

 

애플리케이션에서 발생하는 예외는 크게 Checked ExceptionUnchecked Exception으로 구분된다.

 

▶️ Checked Exception

  • 발생한 예외를 잡아서(catch) 체크한 후 해당 예외를 복구하거나 회피하거나 하는 등의 구체적인 처리를 해야하는 예외이다.
  • 대표적인 예 : ClassNotFoundException

 

 

▶️ Unchecked Exception

  • 예외를 잡아서(catch) 해당 예외에 대한 어떤 처리를 할 필요가 없는 예외이다.
  • 명시적으로 잡아서 어떤 처리를 할 필요는 없다.
  • 대표적인 예 : NullPointerException, ArrayIndexOutOfBoundsException

 

 

코드를 잘못 작성해서 생기는 이런 오류들은 모두 RuntimeException을 상속한 예외들이다.

Java나 Spring에서 수많은 RuntimeException을 지원하지만 RuntimeException을 이용해서

직접 예외(Exception)을 만들어야 하는 경우도 있다.

 

 

 

➤ 의도적으로 예외를 던질 수 (throw) 있는 상황

 

  • 백엔드 서버와 외부 시스템과의 연동에서 발생하는 예외 처리
  • 시스템 내부에서 조회하려는 리소스(자원)가 없는 경우

 

의도적으로 예외를 던져서 클라이언트 쪽에 발생한 에러 정보를 알려줄 수 있다.

 

 

 

➤ 의도적인 예외 throw / catch

 

java에서 throw 키워드는 예외를 메서드 바깥으로 던져준다.

던져진 예외는 메서드 바깥 즉, 메서드를 호출한 지점으로 던져지게 된다.

 

그렇다면 서비스 계층에서 예외를 던지면 어디로 던져질까?

서비스 계층의 메서드는 API 계층인 Controller의 Handler Method가 이용한다.

-> 서비스 계층에서 던져진 예외는 Controller의 Handler Method 쪽에서 잡아서 처리할 수 있다.

 

따라서 Controller에서 발생하는 예외 처리를 위한 Exception Advice 클래스에 서비스 계층에서 던진 예외도 작성해서 처리하면 된다.

 

 

✅ 서비스 계층에서 의도적으로 던질 수 있는 예외는 1가지만 존재하진 않는다.

예를 들어 아래의 경우 모두 예외 상황이 생길 수 있다.

  • 회원 정보가 존재하지 않을 경우
  • 회원 등록 시 이미 존재하는 회원일 경우
  • 로그인 패스워드 검증에서 일치하지 않는 경우

 

이럴 때 handle에러이름Exception()과 같이 메서드 이름을 짓는 것은 적절하지 않다.

그리고 추상적인 RuntimeException 을 그대로 전달 받는 것도 바람직 하진 않다.

 

서비스 계층에서 RuntimeException 을 그대로 throw하고, Exception Advice에서 RuntimeException을 그대로 catch 하는 것은 예외의 의도가 명확하지 않고 구체적으로 어떤 예외가 발생했는지에 대한 정보를 얻는 것이 어렵다.

 

 

 

사용자 정의 예외(Custom Exception) 사용

 

ExceptionCode 를 enum으로 미리 직접 작성해둔다.

BusinessLogicException 클래스를 생성하여 RuntimeException을 상속한다.

super() 상위 클래스 생성자로 RuntimeException에 구체적인 예외 정보 (Exception Code)를 던진다.

 

Exception Advice 클래스에서

메서드 명이 서비스 계층의 비즈니스 로직 처리에서 발생하는 예외를 처리하는 것을 목적으로 한다.

-> handleBusinessLogicException

 

RuntimeException을 파라미터로 전달 받던 것을 BusinessLogicException을 전달 받는 것으로 변경한다.

 

@ResponseStatus(HttpStatus.NOT_FOUND) 제거

ResponseStatus는 고정된 HttpStatus를 지정하므로 다양한 Status를 동적으로 처리할 수 없다.

-> 대신 ResponseEntity를 사용해서 커스텀한 예외 코드(상태)를 전달한다.

 

 

 

▶️ @RestControllerAdvice 에서 ResponseStatus vs ResponseEntity

 

@ResponseStatus : 

한가지 유형으로 고정된 예외를 처리할 경우 HttpStatus를 지정해서 사용

 

@ResponseEntity :

BusinessLogicException 처럼 다양한 유형의 Custom Exception을 처리하고자 할 경우

 

 

▶️ of() 정적 팩토리 메서드

 

new를 직접 사용하지 않고 클래스의 인스턴스를 생성한다.

장점 : 이름을 통해 객체의 의미를 알 수 있다.

 

 

 

 

 

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

 

 

 

 

 

 

Spring MVC에서의 예외 처리

 

 

Spring MVC는 애플리케이션에서 발생할 수 예외를 효율적으로 처리하도록 몇 가지 방법을 제공한다.

 

 

 

➤ @ExceptionHandler를 이용한 Controller 레벨에서의 예외 처리

 

유효성 검증에서 실패했을 때 클라이언트가 전달 받는 Response Body는 애플리케이션에서 예외(Exception)가 발생했을 때, 내부적으로 Spring에서 전송해주는 에러 응답 메시지 중 하나이다.

 

Spring에서 예외는 유효성 검증이 실패했을 때 등 문제가 발생하면 실패를 하나의 예외로 간주하여 이 예외를 던져서 (throw) 예외 처리를 유도한다.

 

 

▶️ @Slf4J

Simple Logging Facade for Java

Logging

  • 개발 중이나 완료 후 발생할 수 있는 오류에 대해 디버깅하거나 운영중인 프로그램 상태를 모니터링 하기 위해 필요한 정보(로그)를 기록하는것

 

Logback과 같은 백엔드 Logging Framework의 facade pattern

 

https://www.slf4j.org/

 

SLF4J

Simple Logging Facade for Java (SLF4J) The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framewor

www.slf4j.org

 

 

▶️ @ExceptionHandler

예외 처리 메서드로 선언

 

 

▶️ .getBindingResult().getFieldErrors()

에러 정보를 확인할 수 있다

 

 

➤ ErrorResponse 클래스 만들기

 

DTO 클래스 유효성 검증 실패 시

실패한 필드에 대한 Error 정보만 담아서 응답으로 전송하기 위한 클래스

 

Response Body의 JSON 응답 객체가 배열이다.

✅ 배열인 이유?

DTO 클래스에서 유효성 검증에 실패하는 멤버변수가 하나 이상이 될 수 있기 때문에 유효성 검증 실패 에러 역시 하나 이상이 될 수 있다.

-> 유효성 검증에 실패한 필드의 에러 정보를 담기 위해서 List 객체를 이용

FieldError라는 별도의 static class를 ErrorResponse 클래스의 안에 정의한다.

FieldError클래스는 ErrorResponse 클래스의 내부(Inner) 클래스라고 부르기 보다는

ErrorResponse 클래스의 static 멤버 클래스라고 부르는 것이 적절하다.

필요한 정보들만 골라서 Response body에 담을 수 있다.

 

 

 

➤ @ExceptionHandler 의 단점

 

1. 코드 중복이 발생한다.

각각의 Controller 클래스에서 @ExceptionHandler 애너테이션을 사용해서 유효성 검증 실패에 대한 에러 처리를 해야한다.

 

2. 예외 마다 에러 처리 핸들러 메서드가 늘어난다.

Controller 클래스 내에서 발생할 수 있는 예외(Exception)은 한가지만 있는게 아니다.

그러면 각각의 예외 마다 @ExceptionHandler를 추가한 에러 처리 핸들러 메서드가 늘어나게 된다.

 

 

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler

 

Web on Servlet Stack

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more com

docs.spring.io

 

 

 

 

❑ @RestControllerAdvice 를 이용한 예외 처리

 

➤ 예외 처리 공통화

 

특정 클래스에 @RestControllerAdvice 애너테이션을 추가하면 여러개의 Controller 클래스에서 @ExceptionHandler, @InitBinder, @ModelAttribute가 추가된 메서드를 공유해서 사용할 수 있다.

 

 

@RestControllerAdvice 애너테이션을 추가한 클래스

➡️ 예외 처리를 모든 Controller에 공통화 할 수 있게 된다.

 

 

▶️ @InitBinder, @ModelAttribute 애너테이션

 

JSP, Thymeleaf 같은 서버 사이드 렌더링(SSR, Server Side Rendering) 방식에서 주로 사용된다.

 

 

 

Controller 클래스에서 @ExceptionHandler 로직 제거

-> ExceptionAdvice 클래스 정의 (@RestControllerAdvice)

-> 모든 Controller의 에러를 동시에 처리할 수 있게 된다.(AOP, 공통 관심사)

 

에러를 정보를 전달하는 객체(Error Response)와 에러 정보를 추출하고 가공하는 static 멤버 클래스로 역할을 분리한다.

 

기능이 늘어남에 따라 Error Response 클래스의 구현 복잡도가 늘어나지만

에러 유형에 따른 에러 정보 생성 역할을 분리함으로써 사용자 입장에서 한층 더 편리해진다.

 

 

▶️ of() 메서드

  • 네이밍 컨벤션(Naming Convention)
  • 객체 생성시 어떤 값들의(of~) 객체를 생성한다는 의미
  • 객체.of(원하는 값)

 

ExceptionAdvice 클래스에서 ErrorResponse 클래스에 of를 사용해서 에러 객체 전달한다.

ResponseEntity 대신 ErrorResponse 객체 그대로 반환하고

@ResponseStatus 애너테이션을 이용해서 attribute로 HTTP Status 전달한다.

 

 

※ @RestControllerAdvice vs @ControllerAdvice

 

SpringMVC 4.3버전 이후부터 @RestControllerAdvice 지원

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

JSON 형식의 데이터를 Response Body로 전송하기 위해서 ResponseEntity로 데이터를 래핑할 필요가 없다.

 

 

 

 

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

 

 

 

 

 

 

 

❑ Service 계층

 

 

Spring MVC layer architecture

 

 

➤ DI를 통한 서비스 계층과 API 계층 연동

 

✔️ API 계층과 서비스 계층을 연동한다?

API 계층에서 구현한 Controller 클래스가 서비스 계층의 Service 클래스와 메서드 호출을 통해 서로 상호작용한다.

 

 

▶️ Service의 의미

 

애플리케이션에서 Service는 도메인 업무 영역을 구현하는 비즈니스 로직과 관련이 있다.

Serivce는 쉽게 말해 비즈니스 로직을 처리하기 위한 Service 클래스이다.

애플리케이션의 서비스 계층은 대부분 도메인 모델을 포함하고 있다.

도메인 모델은 빈약한 모델(anemic domain model)풍부한 도메인 모델(rich domain model)로 구분할 수 있다.

이러한 도메인 모델은 DDD(Domain Driven Design, 도메인 주도 설계)와 관련있다.

(DDD는 현업에서 클래스 설계 경험이 풍부해야 제대로 사용할 수 있다.)

 

 

 

➤ 도메인 엔티티(Domain Entity) 클래스

 

서비스 계층에서 데이터 엑세스 계층과 연동하면서 비즈니스 로직을 처리하기 위해 필요한 데이터를 담는 클래스

보통 리소스 이름으로 만든다. ex) Member, Coffee, Order

 

 

➤ 도메인 엔티티 클래스에 붙는 애너테이션

 

lombok 라이브러리에서 제공한다.

 

@Getter / @Setter

  • getter/setter 메서드를 일일이 작성할 필요 없도록 추가해준다.

 

@AllArgsConstructor

  • 클래스에 존재하는 모든 필드를 매개변수로 갖는 생성자를 자동으로 생성해준다.
  • 만약에 필드 중에 @NotNull 애너테이션이 붙어 있다면 해당 부분은 생성자 내에서 null-check로직을 자동으로 생성한다.

 

@NoArgsConstructor

  • 매개변수가 없는 생성자를 생성한다.
  • 필드에 final 키워드가 있을 경우 초기화 할 수 없기 때문에 에러가 발생한다.
  • 필드에 @NotNull 같은 제약조건이 설정되어 있을 경우, 생성자 내 null-check 로직이 생성되지 않는다.

 

@RequiredArgsConstructor

  • 초기화 되지 않은 모든 final 필드, @NotNull이 설정되 있는 필드들에 대한 생성자를 자동으로 생성해준다.

 

 

 

※ lombok 라이브러리의 더 많은 애너테이션

https://projectlombok.org/features/all

 

Stable

 

projectlombok.org

 

 

 

➤ DI 주입

 

Service 클래스에 @Service 애너테이션 추가해서 Spring Bean으로 등록해주고

Controller 클래스와 Service 클래스간에 느슨한 결합(Losse Coupling)을 위해 DI를 주입해준다.

(Controller는 @RestController 로 이미 Spring Container에 등록되어 있다.)

 

※ 일반적으로 생성자가 하나일 경우에는 @Autowired 를 붙이지 않아도 Spring이 알아서 DI를 적용한다.

하지만 생성자가 하나 이상일 경우, DI를 적용하기 위해선 반드시 @Autowired를 붙여야 한다!

 

 

 

 

❑ Mapper(매퍼)

 

 

➤ Mapper를 이용한 DTO 클래스와 Entity(엔티티) 클래스 매핑

 

▶️ Mapper를 사용하지 않을 경우

 

  • Controller의 핸들러 메서드가 DTO 클래스를 엔티티 클래스로 변환 하는 작업까지 하게 되어 여러 역할을 가지게 된다.
  • 서비스 계층인 엔티티(Entity) 클래스 객체를 클라이언트의 응답으로 전송(API 계층의 역할)해서 계층 간의 역할 분리가 이루어지지 않는다.

 

 

▶️ Mapper 사용법

 

  • 클래스를 Spring Bean으로 등록하기 위해 @Component 애너테이션을 추가한다.
  • 등록된 빈은 Controller에서 사용된다.

 

 

➤ MapStruct를 이용한 Mapper 자동 생성

 

Mapper를 수작업으로 작성하는 것은 비효율 적이다.

Mapper를 자동으로 만들 수 있는 MapStruct을 이용하면 편리하다.

DTO클래스 처럼 Java Bean 규약을 지키는 객체들 간의 변환 기능을 제공하는 매퍼(Mapper) 구현 클래스를 자동으로 생성해주는 코드 자동 생성기이다.

 

MapStruct를 사용하려면 먼저 의존 라이브러리를 추가 해주어야 한다.

Gradle

build.gradle파일 dependencies에 아래 코드 추가한다.

dependencies {

implementation 'org.mapstruct:mapstruct:1.4.2.Final'

annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'

}

 

 

Mapper 인터페이스 추가하고 인터페이스에 @Mapper 애너테이션 추가한다.

-> 인터페이스가 MapStruct의 매퍼 인터페이스로 정의된 것임을 알려준다.

attribute로 componentModel = “spring” 지정해서 Spring Bean으로 등록한다.

MapStruct가 Mapper 인터페이스를 기반으로 매퍼(Mapper) 구현 클래스를 자동으로 생성해준다!

Gradle에 build task를 실행하면 자동으로 생성된다.

intelliJ에서 build task 실행하기

Gradle 탭 -> 해당 프로젝트 명 -> Tasks 디렉토리 -> build 디렉토리 -> build 더블클릭

 

 

 

 

읽어주셔서 감사합니다.

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

 

 

 

 

 

 

DTO(Data Transfer Object)

 

 

 

➤ DTO(데이터 전송 객체)란?

 

  • 마틴 파울러(Martin Fowler)의 저서 ‘Patterns of Enterprise Application Architecture’에서 소개되었다.
  • enterprise application architecture의 한 패턴이다.
  • 계층 간 데이터 교환을 위한 자바 bean을 말한다.

 

 

 

➤ DTO는 왜 나온 것인가?

 

실제로 서비스를 만들다보면 실무에서 Request Param에 많은 양의 정보들을 보낸다.

그렇게 되면 @RequestParam의 개수가 계속 늘어나서 코드가 길어지고 보기 어렵다.

요청 데이터로 받을 정보들을 하나의 객체로 전달한다면 훨씬 코드가 간결해 질 것이다.

바로 DTO가 그런 역할을 해준다.

DTO 클래스가 요청 데이터를 하나의 객체로 전달 받는 역할을 해준다.

 

DTO를 사용하면 @RequestParam 부분이 사라지고 Dto 객체로 대체된다.

 

 

 

 

➤ 데이터 유효성(Validation) 검증의 단순화

 

클라이언트 쪽에서 올바르지 않은 데이터 형식으로 요청할 수 있다.

예를 들면 휴대폰 번호에 문자를 넣거나, 이메일에 @를 안 쓴다거나 이름에 이모티콘을 넣는 등 클라이언트가 우리가 기대하지 않은 값을 전달할 수도 있다.

이럴 때 데이터 유효성을 검사하지 않으면 틀린 형식도 정상적으로 핸들러 메서드에 전달받을 수 있다.

 

서버에서 유효한 데이터를 전달받기 위해 데이터를 검증하는 것을 유효성(Validation) 검증이라고 한다.

 

if문을 이용해서 직접 데이터가 맞는 형식인지 확인해서 틀리면 요청을 거부하도록 작성할 수 있다.

하지만 이 방법은 형식별로 코드가 다르고 복잡하다.

 

HTTP 요청을 전달 받는 핸들러 메서드는 요청을 전달 받는 것이 주 목적이기 때문에 최대한 간결하게 작성되는 것이 좋다.

 

 

 

➤ DTO 클래스 만들기

 

DTO는 타입을 지정할 변수와 getter/setter, 애너테이션으로 구성되어 있다.

DTO 클래스를 사용하면 유효성 검증 로직을 DTO 클래스로 빼내어 핸들러 메서드의 간결함을 유지할 수있다.

 

 

 

 

❑ DTO 유효성 검증

 

핸들러 메서드의 매개변수 중 유효성 검증을 원하는 부분에 작성한 Dto 객체로 매개변수를 작성하고 @Valid 를 타입 앞에 써준다.

@PostMapping
public ResponseEntity postMember(@Valid MemberDto memberDto) {
	...
}

 

@Valid 는 ~Dto 객체에 유효성 검증을 적용하게 해준다.

DTO 유효성 검증의 사용 목적 : 비용이 많이 드는 작업인 HTTP 요청의 수를 줄이기 위함 + 도메인 객체와의 분리

 

 

 

 

 

 

➤ HTTP Request Body가 JSON 형식일 경우

 

 

요청 데이터 🟰 Request Body

 

~PostDto, ~PatchDto 등의 이름으로 dto 클래스 생성

사용하려는 http 메서드에 따라 다르게 명명하면 된다.

 

 

▶️ @RequestBody

 

JSON 형식의 Request Body를 Dto 클래스의 객체로 변환 시켜주는 역할을 한다.

클라이언트에서 전송하는 Request body가 JSON 형식이어야 한다.

JSON 형식이 아닌 다른 형식의 데이터 전송할 경우 Spring 내부에서 ‘Unsupported Media Type’ 과 같은 에러 메시지를 포함한 응답을 전달한다.

 

 

▶️ @ResponseBody

 

JSON 형식의 Response Body를 클라이언트에 전달하기 위해 DTO 클래스의 객체를 Response Body로 변환하는 역할을 한다.

Spring MVC에서 핸들러 메서드에 @ResponseBody 애너테이션이 붙거나 핸들러 메서드의 리턴값이 ResponseEntity 일 경우 내부적으로 HttpMessageConverter 가 동작하여 응답 객체(DTO 클래스의 객체)를 JSON 형식으로 바꿔준다.

 

 

 

➤ JSON Serialization(직렬화) & JSON Deserialization(역직렬화)

 

▶️ Serialization(직렬화)

서버 쪽에서 클라이언트에 응답 데이터로 DTO같은 자바 객체를 JSON형식으로 변환하는 것

Java 객체 ➡️ JSON

 

▶️ Deserialization(역직렬화)

클라이언트에서 JSON 형식의 데이터를 서버에 전송 ➡️ 서버 쪽 애플리케이션에서 전달받은 JSON 형식의 데이터를 DTO 같은 자바 객체로 변환하는 것

JSON ➡️ Java 객체

 

 

 

 

DTO 유효성 검증(Validation)

 

 

➤ DTO 유효성 검증(Validation)이 필요한 이유

 

REST API 통신을 하는 프론트엔드 쪽 웹 앱에서도 자바스크립트를 이용해서 1차적으로 유효성 검사를 진행한다.

그렇다면 왜 굳이 백엔드 애플리케이션에서도 유효성 검증을 할 필요가 있을까?

프론트엔드 쪽에서 유효성 검사를 통과했다고 해서 그 값이 반드시 유효한 값이라는 보장은 없다.

프론트 엔드에서 유효성 검사를 통과한 뒤 HTTP Request 요청이 전송된다.

여기서 자바스크립트로 전송되는 데이터는 개발자 도구를 사용해서 브레이크 포인트(break point)를 건 뒤 얼마든지 그 값을 조작할 수 있기 때문이다.

그렇기에 백엔드 애플리케이션에서도 유효성 검증을 해줄 필요가 있다.

 

 

 

➤ DTO 클래스에 유효성 검증 적용하기

 

유효성 검증을 위한 의존 라이브러리 추가

DTO 클래스에 유효성 검증을 적용하기 위해서 Spring Boot에서 지원하는 Starter가 필요하다.

 

build.gradle 파일의 dependencies 항목에 아래 항목 추가

implementation 'org.springframework.boot:spring-boot-starter-validation'

 

 

 

➤ 유효성 검증에 사용하는 주요 애너테이션

 

▶️ @NotBlank

  • 정보가 비어있지 않은지 검증한다.
  • null 값과 공백(“”), whitspace(“ “) 모두 허용하지 않는다.
  • attribute message로 원하는 에러 메시지를 넣을 수 있다.

 

▶️ @Pattern

  • attribute regexp 로 작성한 정규표현식(Regular Expression)에 매치되는 data인지 검증한다.
  • 유효성 검증에 실패하면 message attribute에 작성한 에러 메시지가 콘솔에 출력된다.

 

 

▶️ @Size(min= , max= )

  • 문자열 또는 배열이 지정된 길이 사이인지 검증한다.

 

▶️ @Max(value) / @Min(value)

  • 값이 value에 적은 값의 이상인지 이하인지 검증한다.

 

▶️ @NotNull

  • null 값이 아닌 경우만 통과한다.

 

▶️ @NotEmpty

  • 공백을 포함해서 길이가 0이면 오류 발생한다.

 

▶️ @Email

  • 유효한 이메일 주소가 포함되어 있지 않을 경우 유효성 검증에 실패한다.

 

 

 

정규식 연습할 수 있는 페이지

(알려주신 페어분께 감사드립니다.ㅎㅎ)

https://regexr.com/

 

RegExr: Learn, Build, & Test RegEx

RegExr is an online tool to learn, build, & test Regular Expressions (RegEx / RegExp).

regexr.com

 

 

 

 

※ CSR(Client Side Rendering) vs SSR(Server Side Rendering)

 

Rendering의 대상은? HTML

 

CSR(Client Side Rendering)

 

SSR(Server Side Rendering)

서버 쪽에서 HTML 렌더링

HTML 페이지를 클라이언트 쪽에 내려준다.

HTML을 템플릿으로 취급, 동적인 데이터를 채워준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

백엔드 측 애플리케이션은 기본적으로 클라이언트 유형과 무관하게

공통의 정보를 제공하도록 디자인이 되어야 한다.

하지만 특정 클라이언트 유형에 맞는 정보를 추가적으로 제공하기 위한 확장을 고려해야 하는 경우도 빈번하다.

 

애플리케이션을 설계할 때 기능을 다 넣어서 만들어 버리면 범위가 너무 넓어져서 구현하기 힘들다.

요구사항에 맞춰서 애플리케이션의 기능을 특정 범위까지만 제한하는 것을

애플리케이션 경계를 설정한다” 고 한다.

 

 

 

 

Spring MVC Architecture

 

 

➤ Spring MVC란?

 

스프링 모듈 중에 웹 계층*을 담당하는 몇가지 모듈이 있다.

 

Sevlet API를 기반으로 클라이언트의 요청을 처리하는 모듈도 있다.

이 모듈이 spring-webmvc 이다.

 

 

✔️ web layer(웹 계층)?

 

Spring MVC layer architecture

 

  • Spring MVC layer architecture의 최상위 계층 이다.
  • 클라이언트의 요청을 받고 응답을 전달하는 계층이다.
  • 다른 계층에서 발생한 예외를 처리한다.
  • 인증을 관리하여 권한이 없는 사용자의 인가를 거부하는 역할도 한다.

 

Spring MVC 레퍼런스

https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html

 

17. Web MVC framework

@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { … } Note, that there is a Model parameter in between Pet and BindingResult. To get this working you have to reor

docs.spring.io

 

 

 

▶️ Servlet 이란?

 

클라이언트의 요청을 처리하도록 규약에 맞추어서 Java 코드로 작성하는 클래스 파일이다.

아파치 톰캣(Apache Tomcat)은 이런 서블릿들이 웹 애플리케이션으로 실행이 되도록 해주는 Servlet Container 중 하나이다.

특징

  • 클라이언트의 요청에 대해 동적으로 작동하는 웹 애플리케이션 컴포넌트
  • html을 사용하여 요청에 응답한다.
  • Java Thread를 이용하여 동작한다.
  • MVC패턴에서 Controller로 이용된다.
  • HTTP 프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속받는다.
  • HTML 변경 시 Servlet을 재컴파일해야 하는 단점이 있다.

 

 

 

 

MVC의 의미

 

 

API 계층

구현하려는 Spring MVC는 계층형 아키텍처에서 API 계층에 해당한다.

 

➤ Model(M)

 

MVC에서 M에 해당한다.

클라이언트에게서 받은 요청을 기반으로 처리한 결과인 데이터Model이라고 한다.

클라이언트에게 응답으로 Model 데이터를 돌려준다.

 

 

➤ View(V)

 

MVC에서 V에 해당한다.

Model 데이터를 이용해서 클라이언트 애플리케이션의 화면에 보여지는 리소스(Resource)를 제공한다.

 

Spring MVC에는 다양한 View기술이 포함되어 있다.

  • HTML 페이지의 출력
  • PDF, Excel 등의 문서 형태로 출력
  • XML, JSON 등 특정 형식의 format으로 변환

 

 

※ JSON(JavaScript Object Notation)이란?

 

데이터를 통신하는 포맷 형식 중의 하나이다.

웹 애플리케이션에서 가장 많이 사용한다.

Spring MVC에서 클라이언트 애플리케이션과 서버 애플리케이션이 주고 받을 때 JSON 형식을 사용한다.

과거에는 XML 형식이 가장 많이 사용되었지만 JSON이 상대적으로 가볍고 복잡하지 않아서 대세이다.

기본 포맷 : {“속성”:”값”}

 

※ 참고) JSON to Java Converter

https://json2csharp.com/code-converters/json-to-pojo

 

JSON to POJO Object Online Converter - Json2CSharp Toolkit

 

json2csharp.com

 

 

 

➤ Controller

 

MVC에서 C에 해당된다.

Controller는 클라이언트 측의 요청을 직접적으로 전달 받는 엔드포인트(Endpoint)이다.

Model과 View의 중간에서 상호 작용을 해주는 역할을 한다.

Model 데이터를 View로 전달하는 역할을 한다.

 

 

➤ Model, View, Controller 간의 처리 흐름

 

Client가 요청 데이터 전송

➡️ Controller가 요청 데이터 수신 ➡️ 비즈니스 로직 처리 ➡️ Model 데이터 생성

➡️ Controller에게 Model 데이터 전달 ➡️ Controller가 View에게 Model 데이터 전달

➡️ View가 응답 데이터 생성

 

 

 

※ Gson 클래스 찾을 수 없는 오류

 

Gradle 방식

스프링 프로젝트 내에 build.gradle로 들어가서

“dependencies” 에  implementation 'com.google.code.gson:gson:2.9.0' 추가 ➡️ gradle reload

 

Maven방식

<dependency>

	<groupId>com.google.code.gson</groupId>

 	<artifactId>gson</artifactId>

 	<version>2.9.0</version>

</dependency>

 

사용하려는 위치에서 import com.google.gson.Gson;

 

 

 

 

Spring MVC 동작 방식과 구성 요소

 

 

Spring MVC 동작방식

 

※ DispatcherServlet의 역할

많은 일을 처리하는 것 같지만 실제로는 요청에 대한 처리는 다른 구성 요소들에게 위임(Delegate)하고 있다.

 

애플리케이션의 가장 앞단에 배치되어 다른 구성 요소들과 상호작용 하면서 클라이언트의 요청을 처리하는 패턴을 ‘Front Controller Pattern’이라고 한다.

 

※ Handler메서드

@GetMapping

@PutMapping

@DeleteMapping

@PatchMapping

@PostMapping

 

 

 

Controller

 

 

➤ 패키지 구조 생성

 

Spring Boot 기반의 애플리케이션에서 주로 사용되는 자바 패키지 구조에는

기능 기반 패키지 구조(package-by-feature)계층 기반 패키지 구조(package-by-layer)가 있다.

 

 

▶️ 기능 기반 패키지 구조(package-by-feature)

 

애플리케이션의 패키지를 구현해야 하는 기능을 기준으로 패키지를 구성하는 것이다.

패키지 안에는 하나의 기능을 완성하기 위한 계층별(API 계층, Service 계층, DataAccess 계층) 클래스들이 모여있다.

상대적으로 테스트와 refactoring이 용이하여 많이 사용한다.

 

 

▶️ 계층 기반 패키지 구조(package-by-layer)

 

패키지를 하나의 계층으로 보고 클래스들을 계층별로 묶는 구조이다.

ex) controller, dto, model, repository, service,...

 

 

 

➤ 실습 예제

 

예제를 따라하면서 정리해보았다.

Mock 데이터를 응답으로 넘기는 형태로 작성하였다.

프론트에 요청과 응답이 정상적으로 통신되는지만 확인하기 위해

실제 안에서 동작하는 로직은 구현하지 않았다.

 

실습 내용 : 커피 주문 애플리케이션 Controller 설계하기

 

▶️ 기능 요구 사항

커피 주문 앱에 넣을 수 있는 기능을 모두 넣으면 구현하기 어렵고 복잡해진다.

따라서 동작에 필요한  기능을 넣어서 먼저 애플리케이션 경계를 설정해야 한다.

  • 주인이 커피 정보를 관리하는 기능
    • 커피 정보 등록 기능
    • 등록한 커피 정보 수정 기능
    • 등록한 커피 정보 삭제 기능
    • 등록한 커피 정보 조회 기능
  • 고객이 커피 정보를 조회하는 기능
    • 커피 정보 조회 기능
  • 고객이 커피를 주문하는 기능
    • 커피 주문 등록 기능
    • 커피 주문 취소 기능
    • 커피 주문 조회 기능
  • 고객이 주문한 커피를 주인이 조회하는 기능
    • 커피 주문 조회 기능
    • 고객에게 전달 완료한 커피에 대한 주문 완료 처리 기능

 

커피 주문 애플리케이션에 필요한 리소스: Member, Coffee, Order

리소스에 해당하는 Controller 클래스를 작성

 

▶️ 엔트리포인트(Entrypoint) 클래스

Spring Boot 기반의 애플리케이션이 정상적으로 실행되려면 main() 메서드가 포함된

애플리케이션의 엔트리포인트를 작성해야 한다.

 

(Spring Initializr을 통해 생성한 프로젝트는 엔트리포인트 클래스가 이미 작성되어 있다.)

 

 

▶️ @SpringBootApplication

  • String Boot의 자동 구성을 활성화한다. (@EnableAutoConfiguration)
  • 애플리케이션 패키지 내에서 @Component 가 붙은 클래스를 검색한 후(scan), Spring Bean으로 등록하는 기능을 활성화 (@ComponentScan)
  • @Configuration 이 붙은 클래스를 자동으로 찾아서 Spring Bean을 등록하는 기능을 활성화 (@SpringBootConfiguration)

 

 

▶️ SpringApplication.run(~.class, arrgs);

  • Spring 애플리케이션을 부트스트랩하고 실행하는 역할

 

 

➤ Controller 만들기

 

▶️ @RestController

  • 클래스에 추가하면 해당 클래스가 REST API의 리소스를 처리하기 위한 API 엔드포인트로 동작함을 의미한다.
  • @RestController가 추가된 클래스는 애플리케이션 로딩 시, Spring Bean으로 등록된다.

 

▶️ @RequestMapping

  • 클라이언트 요청과 클라이언트 요청을 처리하는 Handler Method를 매핑해주는 역할을 한다.
  • 클래스 레벨에 추가하여 클래스 전체에 사용되는 공통 URL(Base URL)을 설정해준다.
  • Attribute
    • value : URL을 넣어준다.
    • produces : 응답 데이터를 어떤 미디어 타입으로 전송할지 설정한다.

 

 

※ 추가학습) URI 작성 기본 규칙

  • 마지막이 ‘/‘로 끝나지 않아야 한다.
  • 동사보다는 명사를 사용한다.
  • 단수형 보다는 복수형 명사를 사용한다.
  • 기본 소문자를 사용한다.
  • 언더스코어(_) 대신에 하이폰(-)을 사용한다.
  • 파일 확장자는 URI에 포함하지 않는다.

 

 

➤ Handler Method 작성하기

 

▶️ @PathVariable

괄호 안에 입력한 문자열 값은 밑의 @~Mapping 의 attribute의 중괄호 안의 문자열과 동일해야 한다.

다르면 “MissingPathVariableException” 발생

 

 

▶️ @PostMapping

클라이언트가 HTTP 메서드 POST를 이용해서 보낸 request body를 서버에 생성할 때 사용, 메서드 위에 작성

  • 원하는 HTTP 메서드에 맞추어서 GetMapping, DeleteMapping, PatchMapping 등 다양하게 지원된다. 반드시 요청하는 HTTP Method 타입과 동일하게 맞춰주어야 정상적으로 매핑된다.
  • 괄호() 안에 몇 가지 Attribute를 넣어줄 수 있다.
  • attribute에 HTTP URI의 일부를 지정해 줄 수 있다.

 

 

▶️ @RequestParam

클라이언트 쪽에서 전송하는 요청 데이터를 쿼리 파라미터(Query Parameter 또는 Query String), 폼 데이터(form-data), x-www-form-urlencoded 형식으로 전송하면 이를 서버 쪽에서 전달 받을 때 사용하는 애너테이션

  • 쿼리 파라미터 : 요청 URL에서 물음표(?)를 기준으로 붙는 key/value 쌍의 데이터를 말한다.
  • 괄호()안에 전달받을 key값 작성
  • 애너테이션이 붙는 변수는 전달받은 value

 

 

 

➤ ResponseEntity

 

클라이언트의 응답으로 전송하는 JSON 형식의 문자열을 직접 타이핑해서 작성하면 문제가 생기기 쉽고  수정이 번거롭다.

ResponseEntity 객체로 리턴값을 변경하면 JSON을 수작업으로 작성할 필요가 없다.

ResponseEntity를 사용하면 JSON 형식에 타이핑할 필요없이 응답 정보를 map에 담아서 보내면 된다.

ResponseEntity 객체를 생성하면서 생성자 매개변수로 응답 데이터(map)와 HTTP 응답 상태를 함께 전달한다.

리턴 값으로 Map 객체 그대로 리턴해도 클라이언트에서 JSON 형식으로 정상적으로 응답 데이터를 받을 수 있지만 ResponseEntity 객체로 응답 데이터 래핑함으로써 더 세련된 방식으로 응답 데이터를 생성할 수 있다.

 

  • HttpEntity의 확장 클래스이다.
  • ResponseEntity에 Custom header를 포함하려면 HttpHeaders에 원하는 헤더를 추가하고 ResponseEntity의 생성자 매개변수로 객체를 전달하면 된다.
  • BodyBuilder 클래스를 이용하면 각각의 항목들(body, header, HttpStatus)을 메서드 체인 방식으로 전달할 수 있다.

 

HTTP 상태 코드

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 

 

 

 

 

프로젝트 코드 깃허브 링크

https://github.com/WiseJade/SpringBoot-practice/tree/main/section2-week3

 

 

 

 

읽어주셔서 감사합니다. 스프링 공부 어려운 데 화이팅하세요!

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

 

 

 

 

 

 

 

❑ 타입별 Advice

 

 

➤ Advice 순서

 

어드바이스는 기본적으로 순서를 보장하지 않는다.

순서를 지정하라면 @Aspect 적용 단위로 org.springframework.core.annotation.@Order 애너테이션을 적용하면 됨

  • 어드바이스 단위가 아닌 클래스 단위로 적용할 수 있다.
  • 하나의 aspect에 여러 어드바이스가 존재하면 순서를 보장받을 수 없다.

aspect를 별도의 클래스로 분리해야 한다.

 

 

 

➤ Advice 종류

 

 

▶️ Before

  • join point 실행 이전에 실행한다.
  • Before Advice 구현한 메서드는 일반적으로 리턴 타입이 void 이다.
  • 메서드에서 예외를 발생시킬 경우 대상 객체의 메서드가 호출되지 않는다.
  • 작업 흐름을 변경할 수 없다.

 

 

▶️ After returning

  • join point가 정상 완료된 후 실행한다.
  • returning 속성에 사용된 이름은 advice 메서드의 매개변수 이름과 일치해야 함

 

 

▶️ After throwing

  • 메서드가 예외를 던지는 경우에 실행한다.
  • 메서드 실행이 예외를 던져서 종료될 때 실행한다.
  • throwing 속성에 사용된 이름은 advice 메서드의 매개변수 이름과 일치해야 함

 

 

▶️ After (finally)

  • join point의 동작(정상 또는 예외)과는 상관없이 실행한다.
  • 메서드 실행 후 공통 기능을 실행한다.
  • try~finally의 finally를 생각하면 된다.
  • 일반적으로 리소스를 해제하는데 사용한다.

 

 

▶️ Around ⭐️

  • 메서드 호출 전후에 수행하며 가장 강력한 advice이다.
  • 메서드 실행 전후, 예외 발생 시점에 공통 기능을 실행한다.
    • 조인 포인트 실행 여부 선택 - joinPoint.proceed()
    • 전달 값 변환 - joinPoint.proceed(args[])
    • 반환 값 변환
    • 예외 변환
  • try~catch~finally 가 들어가는 구문 처리가 가능하다.
  • 어드바이스의 첫 번째 매개변수는 ProceedingJoinPoint를 사용해야 한다.
  • proceed()를 통해 대상을 여러번 실행할 수 있다.

 

 

@Around만 있어도 모든 기능 수행이 가능하다!

하지만 target등 고려해야할 사항이 있을 때 정상적으로 작동되지 않는 경우도 있다.

@Before, @After 어드바이스는 기능은 적지만 원하는대로 작동되고 코드도 단순하다.

 

좋은 설계는 @Around만 사용해서 모두 해결하는 것보다는 제약을 가지더라도 실수를 미연에 방지하는 것이다.

제약을 두면 문제 자체가 발생하지 않고 역할이 명확해진다.

 

 

 

 

 

❑ Pointcut 표현식

 

 

➤ 포인트컷과 표현식&지시자

 

▶️ 포인트컷

관심 조인 포인트를 결정

어드바이스가 실행되는 시기를 제어

AspectJ는 포인트컷을 편리하게 표현하기 위한 표현식을 제공

ex) @Pointcut(“execution(* hello.aop.order..*(..))”)

 

 

▶️ 포인트컷 지시자(Pointcut Designator, PCD)

 

포인트컷 지시자 종류

종류 설명
excution 메서드 실행 조인트 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다
within 특정 타입 내의 조인포인트를 매칭한다
args 인자가 주어진 타입의 인스턴스인 조인 포인트
this 스프링 객체(스프링 AOP proxy) 대상으로 하는 조인 포인트
target Target 객체(스프링 AOP proxy 가르키는 실제 대상) 대상으로 하는 조인 포인트
@target 실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트
@within 주어진 애너테이션이 있는 타입 조인 포인트
@annotation 메서드가 주어진 애너테이션을 가지고 있는 조인 포인트를 매칭
@args 전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트
bean 스프링 전용 포인트컷 지시자, 빈의 이름으로 포인트컷을 지정한다.

 

 

 

➤ Pointcut 표현식 결합

 

&&, ||, ! 를 사용하여 결합할 수 있다.

이름으로 포인트컷 표현식을 참조할 수도 있다.

 

 

▶️ 일반적인 pointcut 표현식들

 

// 모든 공개 매서드 실행
execution(public * *(..))

// set 다음 이름으로 시작하는 모든 메서드 실행
execution(* set*(..))

// AccountService 인터페이스에 의해 정의된 모든 메서드의 실행
execution(* com.xyz.service.AccountService.*(..))

// service 패키지에 정의된 메서드 실행
execution(* com.xyz.servic.*.*(..))

// service 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.xyz.service..*.*(..))



// Spring AOP에서만 메서드 실행하는 표현식들

// service 패키지 내의 모든 조인 포인트
within(com.xyz.service.*)

// service 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트
within(com.xyz.service..*)

// AcountService 프록시가 인터페이스를 구현하는 모든 조인 포인트
this(com.xyz.service.AccountService)

// AcountService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트
target(com.xyz.service.AccountService)

// 단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable 과 같은 모든 조인포인트
args(java.io.Serializable)

// 대상 객체가 @Transactional 애너테이션이 있는 조인 포인트
@target(org.springframework.transaction.annotation.Transactional)

// 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트
@annotation(org.springframework.transaction.annotation.Transactional)

// 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트
@args(com.xyz.security.Classified)

// tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(tradeService)

// 와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(*Service)

 

 

 

 

 

❑ JoinPoint

 

 

➤ AOP 적용 위치

 

  • AOP는 메서드 실행 위치 뿐만 아니라 다양한 위치에 적용할 수 있다.
  • 적용 가능 지점 : 생성자, 필드 값 접근, static 메서드 접근, 메서드 실행
  • AOP 를 수행하는 메서드는 이 JoinPoint 인스턴스를 인자로 받게 된다.
  • JointPoint 인스턴스에서 조인 포인트의 정보를 얻어내야 한다.

 

 

➤ JoinPoint

 

  • AOP를 적용할 수 있는 지점을 의미하는 추상적 개념
  • 프로그램 실행 중 지점을 나타낸다.
  • AspectJ를 사용해서 컴파일 시점과 클래스 로딩 시점에 적용하는 AOP는 바이트코드를 실제 조작하기 때문에 해당 기능을 모든 지점에 적용할 수 있다.
  • 프록시 방식을 사용하는 Spring AOP메서드 실행 지점에만 AOP를 적용할 수 있다.
  • 프록시는 메서드 오버라이딩 개념으로 동작한다.
  • 생성자나 static 메서드, 필드값 접근에는 프록시 개념이 적용될 수 없다. (프록시는 스프링 AOP에서 사용하기 때문)
  • 프록시를 사용하는 스프링 AOP의 조인 포인트는 메서드 실행으로 제한된다.
  • 프록시 방식을 사용하는 스프링 AOP는 스프링 컨테이너가 관리하는 스프링 빈에만 AOP를 적용할 수 있다.
  • JoinPoint 메서드는 advice의 종류에 따라 사용방법이 다르지만 기본적으로 advice 메서드에 매개변수로 선언만 하면 된다.

 

 

 

➤ JoinPoint Interface의 주요 기능

 

  • JoinPoint.getArgs() : JoinPoint에 전달된 인자를 배열로 반환한다.
  • JoinPoint.getThis() : AOP 프록시 객체를 반환한다.
  • JoinPoint.getTarget() : AOP가 적용된 대상 객체를 반환한다. (클라이언트가 호출한 비즈니스 메서드를 포함하는 비즈니스 객체를 반환)
  • JoinPoint.getSignature() : advice되는 메서드에 대한 설명을 반환한다.
  • JoinPoint.toString() : advice되는 방법에 대한 유용한 설명을 인쇄한다.

 

 

➤ ProceedingJoinPoint Interface의 주요 기능

 

▶️ ProceedingJoinPoint Interface

얜 또 뭐야...

https://www.javadoc.io/doc/org.aspectj/aspectjrt/1.7.2/org/aspectj/lang/ProceedingJoinPoint.html

 

ProceedingJoinPoint - aspectjrt 1.7.2 javadoc

Latest version of org.aspectj:aspectjrt https://javadoc.io/doc/org.aspectj/aspectjrt Current version 1.7.2 https://javadoc.io/doc/org.aspectj/aspectjrt/1.7.2 package-list path (used for javadoc generation -link option) https://javadoc.io/doc/org.aspectj/as

www.javadoc.io

 

JoinPoint 인터페이스를 extends하는 인터페이스 이다
  • proceed() : 다음 어드바이스나 타겟을 호출한다.

 

 

 

 

❑ 애너테이션(Annotation)을 이용한 AOP

 

 

➤ Spring에서의 AOP

 

AOP는 스프링 IoC를 보완하여 매우 강력한 미들웨어 솔루션을 제공한다.

 

▶️ Spring AOP 지원

  • @AspectJ 애너테이션 스타일
  • 스키마 기반 접근

 

 

▶️ @AspectJ 지원

 

  • @AspectJ는 애너테이션이 있는 일반 자바 클래스로 aspect를 선언하는 스타일을 말한다.
  • AspectJ 5 릴리스의 일부로 AspectJ 프로젝트에 의해 도입되었다.
  • 스프링은 pointcut 구문 분석 및 일치를 위해 AspectJ가 제공하는 라이브러리를 사용하여 AspectJ 5와 동일한 애너테이션을 해석한다.
  • AOP 런타임은 여전히 순수한 스프링 AOP이며, AspectJ 컴파일러 위버(weaver)에 의존하지 않는다.

 

 

▶️ @AspectJ 지원 활성화

 

  • Spring 설정에서 @AspectJ 에 기반한 Spring AOP 설정과 이러한 aspect에 의해 advice되는 자동 프록시 빈에 대한 Spring 지원을 활성화 해야한다.
  • @AspectJ 지원은 XML 또는 Java 스타일 설정으로 활성화할 수 있다.
  • Java 설정으로 @AspectJ 지원 활성화 방법
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
  • XML 설정으로 @AspectJ 지원 활성화 방법
<aop:aspectj-autoproxy/>

 

 

 

➤ Aspect 선언

 

@AspectJ 지원이 활성화

-> @AspectJ aspect(@Aspect 애너테이션이 있음)이 있는 클래스로 애플리케이션 컨텍스트에 정의된 모든 빈이 Spring에서 자동으로 감지

-> Spring AOP를 구성하는데 사용된다.

 

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AspectExample {

}

 

 

 

➤ Pointcut 선언

  • pointcut 선언은 이름과 매개변수를 포함하는 Signature와 메서드 실행을 정확히 결정하는 pointcut 표현식의 두 부분으로 구성된다.
  • pointcut 표현식은 @Pointcut 애너테이션 사용

 

 

 

 

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

오개념 지적은 환영입니다

 

 

 

 

 

 

 

❑ 관점 지향 프로그래밍(Aspect-Oriented Programming, AOP)

 

 

AOP(관점 지향 프로그래밍)가 OOP(객체 지향 프로그래밍)을 대체? ❎

횡단 관심사(부가 관심사)를 깔끔하게 처리하기 위해 OOP의 부족한 부분을 보조하는 목적으로 생겨난 것이다.

 

부가 관심사

 

 

 

❑ AOP가 필요한 이유

 

 

➤ AOP 기본 개념

 

핵심 기능(Core Concerns) : 업무 로직을 포함하는 기능

부가 기능(CROSS-CUTTING CONCERNS) : 핵심 기능을 도와주는 부가적인 기능 (로깅, 보안, 트랜잭션 등)

Aspect : 부가 기능을 정의한 코드인 Advice와 Advice를 어디에 적용할지 결정하는 포인트컷(PointCut)을 합친 개념 (Advice + PointCut -> Aspect)

 

 

 

➤ 객체 지향 프로그래밍(OOP)

 

  • 공통된 목적을 띈 데이터와 동작을 묶어 하나의 객체로 정의
  • 객체를 적극적으로 활용하여 기능을 재사용할 수 있는 것이 큰 장점
  • 객체를 잘 활용하기 위해선 관심사 분리(Separation of Concerns, SoC)의 디자인 원칙을 준수해야함

 

 

 

➤ OOP의 한계

 

  • 비즈니스 코드에 트랜잭션, 보안, 로깅 등의 코드가 존재하게 된다.
  • 위의 기능은 업무와는 관련없지만 필수적인 부가 기능이라서 빠질 수 없다.
  • 부가 기능은 불특정 다수 클래스에 존재하게 된다.
  • 비즈니스 클래스에 횡단 관심사와 핵심 관심사가 공존하게 된다.
  • 메소드 복잡도 증가 -> 비즈니스 핵심 로직 코드 파악하기 어려움
  • 부가 기능의 불특정 다수 메소드 반복적으로 구현 -> 횡단 관심사의 모듈화가 어려움

 

관심사의 분리는 모듈화의 핵심이다.

OOP만으로는 횡단 관심사 코드를 깔끔하게 분리하고 비즈니스 코드에 적용하기 어려웠다.

이러한 문제를 해결하기 위해 AOP가 등장했다.

 

 

 

➤ AOP의 핵심 기능과 부가 기능

 

 

▶️ 핵심 기능(Core Concerns)

객체가 제공하는 고유의 기능(업무 로직 등)

 

 

▶️ 부가 기능(CROSS-CUTTING CONCERNS)

핵심 기능을 보조하기 위해 제공되는 기능

로그 추적 로직, 보안, 트랜잭션 기능 등

단독으로 사용되지 않고 핵심 기능과 함께 사용됨

 

 ⬇️ 핵심 기능과 부가 기능 모델

핵심 기능과 부가 기능

 

특징

  • 핵심 기능인 XX로직과 부가 기능인 로그 추적 로직이 하나의 객체에 들어간다.
  • 부가 기능이 필요한 경우엔 위와 같이 합쳐져서 하나의 로직을 완성하게 된다.
  • 서비스를 실행하면 핵심 기능과 부가 기능이 함께 실행된다.

 

 

 

➤ 공통으로 사용하는 부가 기능

 

부가 기능은 보통 여러 클래스에서 함께 사용한다.

이러한 부가 기능은 횡단 관심사가 된다.

부가 기능을 여러 곳에 적용하려면 중복 코드가 생기게 되고 그렇게 되면 수정할 때 각각의 클래스를 찾아가면서 수정해야 한다.

 

 

➤ AOP가 필요한 이유

 

소프트웨어에서 변경지점은 하나가 될 수 있도록 잘 모듈화가 되어야 한다.

부가 기능을 애플리케이션 전반에 적용하는 문제는 OOP 방식으로는 해결이 어렵기 때문에 AOP가 필요하다.

 

 

 

 

❑ AOP 기본 용어 정리

 

 

 

AOP 기본 용어

 

 

 

➤ Aspect(애스팩트)

 

여러 객체에 공통으로 적용되는 기능

Advice + Pointcut 을 모듈화하여 애플리케이션에 포함되는 횡단 기능

여러 어드바이스와 포인트컷이 함께 존재함

 

 

 

➤ Join Point (조인 포인트)

 

  • AOP를 적용할 수 있는 모든 지점을 나타내는 추상적인 개념이다.
  • 클래스 초기화, 객체 인스턴스화, 메서드 호출, 필드 접근, 예외 발생과 같은 애플리케이션 실행 흐름에서의 특정 포인트를 의미한다.
  • 애플리케이션에 새로운 동작을 추가하기 위해 조인포인트에 관심 코드(aspect code)를 추가할 수 있다.
  • 횡단 관심은 조인포인트 전/후에 AOP에 의해 자동으로 추가된다.
  • 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메서드 실행 지점으로 제한된다.

 

 

 

➤ Advice(어드바이스)

 

  • join point에서 수행되는 코드를 의미한다.
  • Aspect를 언제 핵심 코드에 적용할 지를 정의한다.
  • 시스템 전체 aspect에 API 호출을 제공한다.
  • 부가 기능에 해당된다.

 

 

 

➤ Pointcut(포인트컷)

 

  • 조인포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능이다.
  • AspectJ 표현식을 사용해서 지정한다.
  • 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능하다.

 

 

 

➤ Weaving(위빙)

 

  • pointcut으로 결정한 target의 joinpoint에 advice를 적용하는 것이다. -> advice를 핵심코드에 적용하는 것
  • 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가할 수 있다.
  • AOP 적용을 위해 aspect 객체에 연결한 상태이다.

 

 

 

➤ AOP 프록시(proxy)

 

  • AOP 기능을 구현하기 위해 만든 프록시 객체이다.
  • 스프링에서 AOP 프록시는 JDK 동적 프록시나 CGLIB 프록시 중 하나이다.

※ 프록시에 대한 자세한 내용은 글 하단부에 작성했다.

 

 

➤ Target(타겟)

 

  • 핵심 기능을 담고 있는 모듈
  • 부가 기능을 적용할 대상
  • Advice 받는 객체, 포인트컷으로 결정

 

 

➤ Advisor(어드바이저)

 

하나의 어드바이스와 하나의 포인트 컷으로 구성된다.

 

 

 

 

❑ 추가 학습

 

 

➤ Spring Proxy(프록시)

 

Spring AOP는 프록시 기반이다.

Spring에서 사용하는 프록시는 JDK dynamic 프록시와 CGLIB 프록시로 두가지다.

메서드를 직접 부르지 않고 프록시를 통해서 메서드를 불러온다.

프록시는 대리자 역할을 한다고 볼 수 있다.

 

 

▶️ JDK Dynamic proxy vs. CGLIB proxy

 

※ JDK Dynamic proxy (JDK 동적 프록시)

  • JDK와 함께 사용할 수 있다.
  • Reflaction을 통해 동적으로 프록시 객체 생성
  • 해당 타겟에 의해 구현된 모든 인터페이스가 프록시 된다.
  • 인터페이스를 구현하는 경우 스프링은 자동으로 JDK 동적 프록시를 사용한다.

 

 

※ CGLIB

  • 스프링이 프록시를 만들기 위해 사용하는 third party library 이다.
  • 클래스 상속을 통해 프록시 객체 생성
  • 구현한 인터페이스가 없을 때 사용한다.

 

 

참고) 프록시 공식 레퍼런스

https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html

 

8.6 Proxying mechanisms

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice). If the target object to be proxied implements at least one interface then a JDK dynamic proxy

docs.spring.io

 

 

 

➤ Spring AOP와 AspectJ

 

 

▶️ Spring AOP

  • 목적 : Spring IoC를 통한 간단한 AOP 구현 -> 완전한 AOP를 의도한 것이 아님
  • Spring Container에 의해 관리되는 bean에만 적용 가능

 

 

▶️ AspectJ

  • 목적 : 완전한 AOP를 제공하는것
  • Spring보다 강력하지만 복잡하다
  • 모든 객체에 적용이 가능하다

 

 

참고) Bealdung 자료 번역 글

https://logical-code.tistory.com/118

 

Spring AOP와 AspectJ 비교하기

Thanks to @ㅅㅈㅎ 님 덕분에 3-5 첫번째 문장의 오타를 수정했습니다. 감사합니다! (더 간편합니다다. ⇢ 더 간편합니다.) @김성수 님 덕분에 3-2. Weaving의 오타를 수정했습니다. 감사합니다! (컴파일

logical-code.tistory.com

 

 

 

 

읽어주셔서 감사합니다.

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

 

 

 

 

 

 

➤ 기본 개념

 

  • Spring은 Spring Container를 통해 객체를 관리한다
  • 스프링 컨테이너에서 관리하는 객체를 bean(빈)이라고 한다
  • @Configuration : 클래스 위에 작성하여 해당 클래스가 IoC 컨테이너에 bean definition 소스로 사용될 수 있음을 알려줌
  • @Bean : 메서드 위에 작성하여 해당 메서드가 스프링 컨테이너에 자동 등록되도록 함

 

@Configuration과 @Bean 공식 문서

https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch03s11.html

 

3.11 Java-based container configuration

3.11.1 Using the @Configuration annotation The central artifact in Spring's new Java-configuration support is the @Configuration-annotated class. These classes consist principally of @Bean-annotated methods that define instantiation, configuration, and in

docs.spring.io

 

 

 

❑ Bean Definition

 

bean definition = 레시피

bean definition은 configuration metadata 정보를 포함하고 있는 인터페이스이다.

configuration metadata

  • 빈을 어떻게 생성할 것인가
  • 빈의 생명주기에 대한 설명
  • 빈의 종속성

 

 

Bean Definition 참고 자료

https://www.tutorialspoint.com/spring/spring_bean_definition.htm

 

Spring - Bean Definition

Spring - Bean Definition The objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. These b

www.tutorialspoint.com

 

BeanDefinition 필드 및 메서드

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanDefinition.html

 

BeanDefinition (Spring Framework 5.3.21 API)

Return the current bean class name of this bean definition. Note that this does not have to be the actual class name used at runtime, in case of a child definition overriding/inheriting the class name from its parent. Also, this may just be the class that

docs.spring.io

 

 

 

 

 

❑ Bean Scope

 

Spring Framework는 6개의 scope를 지원함

그 중에 4개는 ApplicationContext를 사용하는 경우에만 사용할 수 있음

bean은 여러 scope 중 하나를 설정할 수 있음

사용자 정의 scope를 설정할 수도 있음

 

Scop Description
singleton
(Default) Spring IoC 컨테이너당 단일 인스턴스로 bean definition scpoe를 지정
prototype 스프링 컨테이너가 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프
request 요청이 들어오고 나갈 까지 유지되는 스코프
session 세션이 생성되고 종료될 때까지 유지되는 스코프
application ServletContext 라는 클래스 타입의 객체로 생성되어 서버가 종료될 때까지 유지
websocket 단일 bean definition 범위를 WebSocket 라이프사이클까지 확장함
Spring ApplicationContext 컨텍스트에서만 유효함

 

 

 

➤ 스프링 없는 컨테이너만 사용한 객체 생성방법

 

같은 클래스의 여러 인스턴스를 만들면 서로 다른 주소값을 가진다.

수많은 객체를 생성하게 되면 이 방식은 메모리 낭비가 생기고 효율성이 떨어지게 됨

-> 해결 방법으로 싱글톤 패턴을 사용할 수 있음

 

 

 

 

❑ Singleton Scope

 

싱글톤 패턴 : 클래스의 인스턴스를 단 1개만 생성하도록 하는 디자인 패턴

 

스프링 컨테이너의 시작과 함께 인스턴스가 생성되어 스프링 컨테이너가 종료될 때까지 유지됨

싱글톤 빈의 하나의 공유 인스턴스만 관리

생성자를 private로만 만들어서 외부에서 임의의 new로 인스턴스를 만드는 것을 방지

스프링 컨테이너 종료시 소멸 메서드도 자동으로 실행됨

 

 

 

➤ 싱글톤?

 

싱글톤은 해당 빈의 인스턴스를 오직 하나만 생성해서 사용하는 것을 의미함

단일 인스턴스는 싱글톤 빈의 캐시에 저장됨

이름이 정해진 빈에 대한 모든 요청과 참조는 캐시된 개체를 반환함

싱글톤 스코프의 스프링 빈은 여러번 호출해도 모두 같은 인스턴스의 참조 주소값을 가짐

 

 

▶️ 싱글톤 디자인 패턴은 어떤 때 사용할까?

 

예를 들어 CodeStates라는 회사 클래스와 크루, 수강생 클래스가 있다.

크루와 수강생은 여러 명일 수 있으므로 인스턴스를 여러 개 생성하면 되지만

CodeStates는 어떤 크루나 수강생 객체에서 참조해도 동일한 회사이므로 하나의 인스턴스만 생성해도 된다.

 

싱글톤 예시

 

싱글톤 패턴 적용 코드

public class CodeStates {
	// static 영역에 단일 객체 생성
    private static final CodeStates instance = new CodeStates();
	// getInstance 메서드로만 인스턴스 호출
    public static CodeStates getInstance() {
        return instance;
    }
    // 생성자를 private 으로 선언하여 외부에서 new 키워드로 인스턴스 생성하는 것을 방지
    private  CodeStates() {
    }
    public void getInfo() {
        System.out.println("CodeStates 입니다.");
    }
}

 

 

▶️ 코드 특징

  • static 영역에 객체 인스턴스를 미리 1개 생성함
  • 객체 인스턴스가 필요한 경우 getInstance() 메서드를 통해서만 조회
  • 항상 같은 인스턴스가 호출됨
  • 외부에서 생성자를 new로 새로 만드는 것을 방지하기 위해 private으로 생성자를 생성함

 

스프링 컨테이너의 기본값은 싱글톤이기 때문에 위 코드처럼 직접 static 인스턴스를 만들지 않아도 됨

 

 

 

➤ 싱글톤 패턴의 문제점

 

  • 싱글톤 패턴을 구현하는 코드 자체는 많음
  • 의존 관계상 클라이언트가 구체 클래스에 의존함
  • 지정해서 가져오기 때문에 테스트하기 어려움
  • private 생성자를 사용하여 자식 클래스를 만들기 때문에 유연성이 떨어짐
  • 속성 공유
  • Application 초기 구동 시 인스턴스 생성

 

 

 

➤ 싱글톤 패턴 문제 해결

 

싱글톤 패턴의 문제는 스프링 컨테이너로 해결할 수 있다.

스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.

싱글톤 객체로 생성하고 관리하는 기능을 싱글톤 레지스트리라고 함

스프링 컨테이너의 위 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하고 객체를 싱글톤으로 유지할 수 있음

 

싱글톤 컨테이너로 관리하려는 객체의 클래스에 @Configuration(클래스 위에 작성)와 @Bean(메서드 위에 작성)을 작성해준다.

@Bean을 통해 스프링 컨테이너에 등록됨

 

 

 

➤ 싱글톤 방식 주의점

 

여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 몇가지 조심할 부분이 있다.

상태를 유지하게(stateful) 설계하면 안된다. 무상태(stateless)로 설계해야 한다.

  • 특정 클라이언트가 값을 변경할 수 있으면 안됨
  • 특정 클라이언트에서 의존적인 필드가 존재하면 안됨
  • 클라이언트가 수정할 수 있으면 안됨, 읽기만 가능해야함
  • 스프링 빈의 공유 값을 설정하면 장애가 발생할 수 밖에 없음 -> 공유되지 않는 지역변수, 매개변수 사용

 

 

 

싱글톤 이외의 다른 Scope Reference

https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-scopes

 

5. The IoC container

The BeanFactory provides the underlying basis for Spring's IoC functionality but it is only used directly in integration with other third-party frameworks and is now largely historical in nature for most users of Spring. The BeanFactory and related interfa

docs.spring.io

 

 

 

 

 

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

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

 

 

 

 

 

 

❑ Spring Container

 

 

 

스프링 프레임워크의 핵심 컴포넌트

스프링 컨테이너는 내부에 존재하는 애플리케이션 빈의 생명주기(생성, 관리, 제거 등)를 관리한다.

 

 

▶️ 스프링 컨테이너가 무엇일까?

 

Spring Container

 

ApplicationContext를 스프링 컨테이너라고 한다.

인터페이스로 구현되어있다. (다형성 적용)

 

스프링 컨테이너는 XML, 애너테이션 기반의 자바 설정 클래스로 만들 수 있다.

예전에는 xml을 통해 모두 설정해줬지만 복잡한 설정들을 Spring Boot를 사용하면서 거의 사용하지 않게되었다.

빈의 인스턴스화, 구성, 전체 생명주기, 제거까지 처리한다.

컨테이너는 개발자가 정의한 Bean을 객체로 만들어 관리하고 개발자가 필요할 때 제공한다.

원하는 만큼 많은 객체를 가질 수 있다.

의존성 주입을 통해 애플리케이션의 컴포넌트를 관리한다.

  • 스프링 컨테이너는 서로 다른 빈을 연결해 애플리케이션의 빈을 연결하는 역할을 한다.
  • 개발자는 모듈 간에 의존 및 결합으로 인해 발생하는 문제로부터 자유로울 수 있다.
  • 메서드가 언제, 어디서 호출되어야하는지, 메서드 호출에 필요한 매개변수를 준비해서 전달하지 않는다.

 

 

▶️ 왜 스프링 컨테이너를 사용할까?

 

new 생성자를 사용해서 객체를 생성할 필요가 없다. -> 낮은 결합도

객체간의 참조가 많을수록 의존성이 높아진다.

높은 의존성은 객체지향 프로그래밍의 핵심 중 하나인 낮은 결합도를 지키지 못하게 한다.

그래서 의존성을 낮추기 위해 스프링컨테이너가 필요하다.

 

 

▶️ 기존 방식 vs Spring Container

 

  • 기존의 방식 : 새로운 정책이 생길 때 마다 변경 사항을 일일이 수정해야 했다. 작업 초반부는 하나하나 수정할 수 있지만 코드가 거대해질 경우 의존도가 높아져있고 그에 따른 코드 변경 시간이 많이 들어간다.
  • Spring Container : 구현 클래스에 있는 의존을 제거하여 인터페이스에만 의존하도록 설계한다. 거대한 코드를 수정할 때도 일부분만 수정하면된다.

 

 

▶️ 스프링 컨테이너의 생성 과정은 어떻게 될까?

 

주로 사용하게 되는 설정방식은 Java 기반의 애너테이션 기반 설정이다.

(하지만 XML 방식에 대해서도 이해를 하고 있어야 한다.)

 

참고) XML 이란 무엇인가?

XML(eXtensible Markup Language) W3C에서 개발된, 다른 특수한 목적을 갖는 마크업 언어를 만드는데 사용하도록 권장하는 다목적 마크업 언어

다른 많은 종류의 데이터를 기술하는데 사용할 수 있다.

주로 다른 종류의 시스템, 특히 인터넷에 연결된 시스템끼리 데이터를 쉽게 주고 받을 수 있게하여 HTML의 한계를 극복

텍스트 데이터 형식으로 유니코드를 사용(전 세계 언어 지원)

지금은 임의의 자료구조를 나타내는데 널리 사용됨 (대표적인 예가 웹 서비스)

- 위키백과 -

 

 

Configuration Metadata 사용

※ Configuration Metadata 가 무엇일까?

⬇️ 공식문서

https://docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html

 

Configuration Metadata

Configuration metadata files are located inside jars under META-INF/spring-configuration-metadata.json. They use a JSON format with items categorized under either “groups” or “properties” and additional values hints categorized under "hints", as sh

docs.spring.io

 

스프링 컨테이너는 매개변수로 넘어온 설정 클래스 정보를 사용하여 스프링 빈을 등록한다.

new AnnotationConfigApplicationContext(구성정보.class) 로 스프링에 있는 @Bean의 메서드를 등록

애너테이션 기반의 자바 설정 클래스로 Spring을 만드는 것을 의미한다.

 

XML 기반으로 만드는 ClassPathXmlApplicationContext도 있다.

XML 기반 메타데이터의 기본 구조

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>


    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>


     <!-- more bean definitions go here -->

</beans>

 

<beans id=“...”> : 빈 정의를 식별하는데 사용되는 문자열

<beans class=“...”> : 빈의 유형을 정의하고 클래스 이름을 사용

 

 

 

➤ 스프링 컨테이너의 종류

 

▶️ BeanFactory

스프링 컨테이너의 최상위 인터페이스

빈을 등록하고 생성, 조회하고 돌려주는 등 빈을 관리하는 역할

getBean() 메서드를 통해 빈을 인스턴스화 할 수 있음

@Bean이 붙은 메서드 명을 스프링 빈의 이름으로 사용하여 빈 등록

 

 

▶️ ApplicationContext

BeanFactory의 기능을 상속함

BeanFactory 기능 + 부가기능 제공

참고) 부가기능

  • MessageSource : 메세지 다국화를 위한 인터페이스
  • EnvironmentCapable : 개발, 운영 등 환경변수 등으로 나눠 처리, 애플리케이션 구동 시 필요한 정보들을 관리하는 인터페이스
  • ApplicationEventPublisher : 이벤트 관련 기능을 제공하는 인터페이스
  • ResourceLoader : 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회

 

 

※ 컨테이너 인스턴스화

ApplicationContext 생성자에 제공된 위치 경로는 컨테이너가 로컬 파일 시스템, Java CLASSPATH 등과 같은 다양한 외부 리소스로부터 구성 메타데이터를 로드할 수 있도록 하는 리소스 문자열

 

 

➤ new와 생성자 주입 코드 차이점

 

new를 사용하는 대신 생성자를 통해 의존 객체가 주입되고 느슨한 의존 관계가 이루어짐 -> 객체지향적 프로그래밍 가능

구현 클래스는 Bean 설정에 따라 유연하게 변하게

생성자가 주입된 객체 입장에서는 어떤 구현 객체가 주입될 지 알 수 없고 알 필요도 없음

어떤 객체가 주입될 지는 외부에서 결정

생성자가 주입된 객체는 오로지 실행에만 집중하게 됨

 

 

 

❑ Bean(빈)

 

➤ 빈이란?

 

스프링 컨테이너에 의해 관리되는 재사용 소프트웨어 컴포넌트이다.

 

Spring 컨테이너가 관리하는 자바 객체를 의미하며 하나 이상의 빈을 관리한다.

빈은 인스턴스화된 객체를 의미함

스프링 컨테이너에 등록된 객체를 스프링 빈이라고 함

 

참고) Spring Bean != Java Bean

자바 빈

  • private로 선언된 필드와 Getter/Setter로만 이루어져 있다
  • 생성자가 없는 no-Argument 생성자만 존재한다
  • java.io.Serializable을 구현한다

스프링 빈은 스프링 컨테이너에 등록된 객체를 의미한다.

 

@Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록함

빈은 클래스의 등록정보, Getter/Setter 메서드를 포함

빈은 컨테이너에 사용되는 설정 메타데이터로 생성됨

설정 메타데이터

  • XML / 자바 애너테이션, 자바코드로 표현함
  • 컨테이너의 명령과 인스턴스화, 설정, 조립할 객체를 정의함

 

 

➤ Bean 접근방법

 

ApplicationContext를 사용하여 bean 정의를 읽고 액세스할 수 있음

  • getBean을 사용하여 bean의 인스턴스를 가져올 수 있음
  • ApplicationContext 인터페이스는 bean을 가져오는 몇 가지 방법들이 있음
  • 응용프로그램 코드에서는 getBean() 메서드로 호출하여 사용하면 안된다. (IoC/DI)

 

 

➤ BeanDefinition

 

스프링은 다양한 설정 형식을 BeanDefinition이라는 추상화 덕분에 지원할 수 있음

빈은 BeanDefinition(빈 설정 메타정보)으로 정의되고 BeanDefinition에 따라서 활용하는 방법이 달라짐

BeanDefinition(빈 설정 메타정보)

  • 속성에 따라 컨테이너가 Bean을 어떻게 생성하고 관리할지 결정함
  • @Bean / <bean> 당 1개씩 메타 정보가 생성됨
  • Spring이 설정 메타정보를 BeanDefinition 인터페이스를 통해 관리하기 때문에 컨테이너 설정을 XML이나 Java코드로 설정할 수 있음(스프링 컨테이너는 설정 형식이 무엇인지 모르고 BeanDefinition만 알면 됨)

 

 

 

 

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

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

 

1 2 3 4 5 6