no image
@Transactional
일단 @Transactional 을 사용하기 전 트랜잭션에 대해 알아야 한다 트랜잭션에 대해 한번 알아보자. 👉트랜잭션(transaction) 정의 트랜잭션(transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다. 👉트랜잭션 특징 트랜잭션은 데이터베이스 시스템에서 병행 제어 및 회복 작업 시 처리되는 작업의 논리적 단위이다. 사용자가 시스템에 대한 서비스 요청시 시스템이 응답하기 위한 상태 변환 과정의 작업단위이다. 하나의 트랜잭션은 Commit되거나 Rollback된다. 👉트랜잭션(transaction) 성질 원자성 (Atomicity) 한 트랜잭션 내에서 실행한 작업들은 하나로 본다. 모두 성..
2021.08.24
no image
Spring REST Docs
➡️Spring Rest Docs 프로젝트를 하다 보면 같이 프로젝트를 진행하는 팀원들에세 API문서를 제공할 일이 많다.그런데 직접API를 위키나 문서에 정리하게 되면 코드와 싱크를 맞추기가 힘들다.대부분 API코드가 변경되면 문서를 잘 변경하지 않는다.이러한 문제를 해결해 주는 게 Spring REST Dosc이다. Spring REST Docs는 test code 기반으로 아래와 같이 문서를 생성해 준다.Test code 기반으로 문서가 생성되기 때문에 코드가 변경 되더라고 코드 기반으로 문서를 자동으로 업데이트하기 때문에 코드와 문서에 싱크 문제도 존재하지 않는다. 이러한 이유로 Rest docs는 API 문서로 사용하는데 아주 좋다. Build gradle 설정 하기 1.Asciidoctor p..
2021.08.23
no image
DAO,DTO,Entity Class
➡️DAO 란? Data Access Object의 약자로 실제로 DB에 접근하는 객체이다. 서비스와 DB를 연결하는 역할을 한다. SQL을 사용하여 DB에 접근한 후 적절한 CRUD API를 제공한다.(ex:JPA) ➡️DTO 란? Data Transfer Object 의 약자로 계층간 데이터 교환을 위한 객체이다. DB에서 데이터를 얻어 서비스나 컨트롤러 등으로 보낼 때 사용하는 객체를 말한다. 로직을 갖고 있지 않은 순수한 데이터 객체이며,getter/setter메서드만 갖는다. 그러나 DB에서 꺼낸 값을 임의로 변경 할 필요가 없기 때문에 DTO클래스에는 setter가 없다 그대신 생성자에서 값을 할당한다. Request와 Response용 DTO는 View를 위한 클래스이다. 자주 변경이 필요한..
2021.08.13
no image
@RequestMapping에 대하여
요청이 들어오면 특정 메서드와 요청하기 위해 사용하는 것이 @RequestMapping이다. @RequestMapping에서 가장 많이 사용하는 것은 value와 method 이다. value는 요청 받을 url을 설정할 수 있다. method는 어떤 요청을 받을지 정의한다.(GET,POST,DELETE,PUT,PATCH 등 ) /v1 url로 매핑하는 예제 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @RestController public class ControllerV1 { @RequestMapping(value = "/v1", method = RequestMethod.GET) public String getM..
2021.07.24
no image
[Spring]@RestController 와 @Controller의 차이
➡️Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있다. 전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차이점은 HTTP Response Body가 생성되는 방식에서 나타난다. ➡️@Controller SpringMvc 컨트롤러인 @Controller는 주로 View를 반환하기 위해 사용한다. 클라이언트는 URI형식으로 웹 서비스애 요청을 보낸다. DispatcherServlet이 요청을 받아 매핑되는 핸들러와 그 type을 찾아 준다. 다음은 요청한 뷰 이름으로 viewResolver를 찾는다. viewResolver는 뷰의 논리 이름을 물리 이름으..
2021.07.18
no image
로깅(Logging)
➡️ 로깅(logging)이란? 정보를 제공하는 일련의 기록인 로그(log)를 생성하도록 시스템을 작성하는 활동이다. 프린트 줄 넣기(printlning)는 간단한, 보통은 일시적인, 로그를 생성하기만 한다. 시스템 설계자들은 시스템의 복잡성 때문에 로그를 이해하고 사용해야 한다. 로그가 제공하는 정보의 양은, 이상적으로는 프로그램이 실행되는 중에도, 설정 가능해야한다. 일반적인 로그 기록의 이점 로그는 재현하기 힘든 버그에 대한 유용한 정보를 제공할 수 있다. 로그는 성능에 관한 통계와 정보를 제공할 수 있다. 설정이 가능할 때, 로그는 예기치 못한 특정 문제들을 디버그하기 위해, 그 문제들을 처리하도록 코드를 수정하여 다시 적용하지 않아도, 일반적인 정보를 갈무리할 수 있게 한다. ➡️로그를 출력하는..
2021.07.13
no image
[Spring]다양한 의존관계 주입 방법
의존관계 주입은 크게 4가지 방법이 있다. 수정자 주입 (setter 주입) 필드 주입 일반 메서드 주입 생성자 주입 👉수정자 주입(setter 주입) setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다. 선택,변경 가능성이 있는 의존관계에 사용한다. 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class OrderServiceImpl implements OrderService{ private MemberRepository memberRepository; private DisCountPolicy discount..
2021.06.25
no image
[Spring]의존성 주입(Dependency Injection)
➡️의존성 주입이란 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법이다. 예를 들어 장난감,리모컨,휴대폰 모두 배터리를 교체 할 수 있는 제품이라면 장난감,리모컨,휴대폰 모두 배터리에 의존하기 때문에 배터리를 갈아끼우는 것을 의존성 주입이라고 보면 된다. 그러나 만약 장난감이나 휴대폰이 배터리 교체가 불가능한 일체형이라면 교체하지 못하고 새제품으로 바꿔야 하기때문에 설계로 본다면 유연하지 못한 설계가 된다. 밑에 예제를 보고 의존성 주입이 필요한 이유를 알아보자 1 2 3 public class Americano{ } cs 1 2 3 4 5 6 7 8 9 10 11 12 public class Cafe{ private Americano americano; pub..
2021.06.24

@Transactional

ryudjae
|2021. 8. 24. 04:20
728x90

일단 @Transactional 을 사용하기 전  트랜잭션에 대해 알아야 한다 트랜잭션에 대해 한번 알아보자.

 

👉트랜잭션(transaction) 정의

  • 트랜잭션(transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

👉트랜잭션 특징

  • 트랜잭션은 데이터베이스 시스템에서 병행 제어 및 회복 작업 시 처리되는 작업의 논리적 단위이다.
  • 사용자가 시스템에 대한 서비스 요청시 시스템이 응답하기 위한 상태 변환 과정의 작업단위이다.
  • 하나의 트랜잭션은 Commit되거나 Rollback된다.

👉트랜잭션(transaction) 성질

  • 원자성 (Atomicity)
    • 한 트랜잭션 내에서 실행한 작업들은 하나로 본다. 모두 성공하거나 모두 실패
  • 일관성 (Consistency)
    • 트랜잭션은 일관성 있는 데이터베이스 상태를 유지한다.
  • 격리성 (lsolation)
    • 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리해야 한다.
  • 영속성,지속성 (Durability)
    • 트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 한다.

 

👉트랜잭션(transaction) 연산 및 상태 

  • Commit연산
    • Commit연산은 한개의 논리적 단위에 대한 작업이 성공적으로 끝났고 데이터베이스가 다시 일관된 상태에 있을 때, 이 트랜잭션이 행한 갱신 연산이 완료된 것을 트랜잭션 관리자 에게 알려주는 연산이다.
  • Rollback연산
    • Rollback연산은 하나의 트랜잭션 처리가 비정상적으로 종료되어 데이터베이스의 일관성을 깨트렸을 때, 이 트랜잭션의 일부가 정상적으로 처리되었더라도 트랜잭션의 원자성을 구현하기 위해 이 트랜잭션이 행한 모든 연산을 취소하는 연산이다.
    • Rollback시에는 해당 트랜잭션을 재시작하거나 폐기한다.

 


이제 스프링에서 트랜잭션 처리를 지원하는데 그중 어노테이션 방식으로 @Transactional을 선언하여 사용하는 방법을 알아보자.

기본적인 적용방식 

이런식으로 클래스나 메서드 위에 @Transactional이 추가되면 ,이 클래스나 메서드에 트랜잭션 기능이 적용된 프록시 객체가 생성된다.

이 프록시 객체는 @Transactional이 포함된 메서드가 호출 될 경우, PlatformTransactionManager를 사용하여 트랜잭션을 시작하고 ,정상여부에 따라 Commit 또는 Rollback한다.

적용방식은 위 사진처럼 하면 된다. 

 

이제 다수의 트랜잭션이 경쟁시 발생할 수 있는 문제를 알아보자

 

  1. Dirty read
    • 트랜잭션1 이 어떠한 값을 A에서 B로 변경하고 아직 커밋하지 않은 상황에서 트랜잭션2가 같은 값을 읽는 경우 트랜잭션2는 값을 조회하면 B가 조회된다.(만약 트랜잭션1이 롤백을 하게되면 트랜잭션2는 잘못된 값을 읽은 것이다.)
    • 위 같은 상황을 트랜잭션이 완료되지 않은 상황에서 데이터에 접근을 허용할 경우 발생할 수 있는 데이터 불일치 이다.
  2. Non-Repeatable Read
    • 트랜잭션1이 A라는 값을 읽고, 이후 A는 같은 쿼리를 실행할 예정인데, 그 사이에 트랜잭션 2가 값을 B로 바꾸고 커밋해버리면 트랜잭션1이 같은 쿼리를 두번 날리는 사이 두 쿼리의 값은 달라진다.
    • 한 트랜잭션에서 같은 쿼리를 투번 실행했을 경우 발생할 수 있는 데이터 불일치이다.

 

 

위와 같은 문제들을 해결할 수 있는 속성에 대해서 알아보자.

@Transactional속성

1.lsolation(격리 수준)

  • DEFAULT
    • 기본 격리 수준
  • READ_COMMITED  
    • 트랜잭션이 커밋 된 확정 데이터만 읽기 허용(Dirty read 방지)
  • REPEATABLE_READ
    • 트랜잭션이 완료될 때까지 SELECT문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.(Non-Repeatable read 방지)
    • 행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능 하기때문에 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다.
  • SERIALIZABLE
    • 트랜잭션이 완료될 때까지 SELECT문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다

 

2.readOnly(읽기 전용)

  • 트랜잭션을 읽기 전용으로 설정할 수 있다.
  • 성능을 최적화하기 위해 사용할 수도 있고 특정 트랜잭션 작업 안에서 쓰기 작업이 일어나는 것을 의도적으로 방지하기 위해 사용할 수도 있다.
  • 읽기 전용 트랜잭션이 시작된 이후 INSERT, UPDATE, DELETE 같은 쓰기 작업이 진행되면 예외가 발생한다. 
  • ex: @Transactional(readOnly = true)

3. 트랜잭션 롤백 예외(rollback-for, rollbackFor, rollbackForClassName) 

  • 선언적 트랜잭션에서는 런타임 예외가 발생하면 롤백한다.
  • 예외가 전혀 발생하지 않거나 체크 예외가 발생하면 커밋한다.   체크 예외를 커밋 대상으로 삼은 이유는 체크 예외가 예외적인 상황에서 사용되기보다는 리턴 값을 대신해서 비즈니스적인 의미를 담은 결과를 돌려주는 용도로 많이 사용되기 때문이다.
  • 스프링에서는 데이터 엑세스 기술의 예외는 런타임으로 전환돼서 던져지므로 런타임 예외만 롤백 대상으로 삼은것이다.

  • rollbackFor 속성설정
    • 예: @Transactional(rollbackFor=Exception.class)
    • 특정 예외가 발생 시 강제로 Rollback
  • noRollbackFor 속성설정
    • 예: @Transactional(noRollbackFor=Exception.class)
    • 특정 예외의 발생 시 Rollback 처리되지 않음

4.전파 옵션 (propagation)

 

  • 트랜잭션 동작 도중 다른 트랜잭션을 호출(실행)하는 상황이에 선택할 수 있는 옵션이다.
  • @Transactional의 propagation 속성을 통해 피호출 트랜잭션의 입장에서는 호출한 쪽의 트랜잭션을 그대로 사용할 수도 있고, 새롭게 트랜잭션을 생성할 수도 있다
  • REQUIRED
    •  디폴트 속성, 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성한다.
  • SUPPORTS
    •  이미 시작된 트랜잭션이 있으면 참여하고 그렇지 않으면 트랜잭션 없이 진행하게 만든다. 
  • REQUIRES_NEW
    •   부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성
  • MANDATORY
    •  REQUIRED와 비슷하게 이미 시작된 트랜잭션이 있으면 참여한다. 
    •  반면에 트랜잭션이 시작된 것이 없으면 새로 시작하는 대신 예외를 발생시킨다. 
    •  혼자서는 독립적으로 트랜잭션을 진행하면 안 되는 경우에 사용한다.
  • REQUIRES_NEW
    •  항상 새로운 트랜잭션을 시작한다.
    •  이미 진행 중인 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.
  • NOT_SUPPORTED
    •  트랜잭션을 사용하지 않게 한다.
    •  이미 진행 중인 트랜잭션이 있으면 보류시킨다.
  • NEVER
    • 트랜잭션을 사용하지 않도록 강제한다.
    • 이미 진행 중인 트랜잭션도 존재하면 안된다 있다면 예외를 발생시킨다.
  • NESTED
    • 이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다.
    •  중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만드는 것이다.




5.timeout속성

  • 지정한 시간 내에 해당 메소드 수행이 완료되이 않은 경우 rollback 수행. -1일 경우 no timeout(Default=-1)
  • ex) @Transactional(timeout=10)

 

 

728x90

'Dev > Spring' 카테고리의 다른 글

[Spring] AOP(Aspect Oriented Programming)  (0) 2021.09.10
ResponseEntity  (0) 2021.08.25
Spring REST Docs  (0) 2021.08.23
DAO,DTO,Entity Class  (0) 2021.08.13
@RequestMapping에 대하여  (0) 2021.07.24

Spring REST Docs

ryudjae
|2021. 8. 23. 23:39
728x90

➡️Spring Rest Docs

프로젝트를 하다 보면 같이 프로젝트를 진행하는 팀원들에세 API문서를 제공할 일이 많다.그런데 직접API를 위키나 문서에 정리하게 되면 코드와 싱크를 맞추기가 힘들다.대부분 API코드가 변경되면 문서를 잘 변경하지 않는다.이러한 문제를 해결해 주는 게 Spring REST Dosc이다.

Spring REST Docs는 test code 기반으로 아래와 같이 문서를 생성해 준다.Test code 기반으로 문서가 생성되기 때문에 코드가 변경 되더라고 코드 기반으로 문서를 자동으로 업데이트하기 때문에 코드와 문서에 싱크 문제도 존재하지 않는다.

이러한 이유로 Rest docs는 API 문서로 사용하는데 아주 좋다.

Build gradle 설정 하기

1.Asciidoctor plugin

2.asciidoctor 구성에 spring-restdocs-asciidoctor 디펜던시를 추가한다. 이건 자동적으로 내 프로젝트에서 .adoc파일들을 읽어 build/generated-snippets에  파일을 읽어 HTML문서로 export한다.

 

3.Spring mvc rlqksdp  snippets을 뽑고싶다면 해당 의존성을 추가해주면 된다.

 

4.(ext) snippets의 생성될 위치를 지정해준다.

5.(test) 테스트 테스크가 실행될때 snippets들이 생성 위치에 떨어지게 설정해준다.

6.(asciidoctor) asciidoctor 테스크를 구성 해준다.

 

bootJar

7.문서가 만들어지기전에 test가 실행되도록 의존성을 걸어준다.

8.jar 생성전에 asciidoctor 테스크가 실행되도록 설정해준다.

9.jar static 폴더에 rest docs가 HTML로 생성되면 넣어준다.

 

Spring REST Docs Test code 작성

 

유저를 생성하는 컨트롤러 코드 

 

 

controller단위 테스트와 rest docs를 생성하도록 작성한 테스트 코드이다.

-setup 메소드는 Rest docs를 작성하기 위해 테스트 코드 실행 전 실행되는 메소드로 설정한다.

RestDocumentationContextPrevider 은 MVC rest docs 를 세팅할 때 사용한다.

그리고 아래처럼 테스트 코드 내부에 field문서를 작성해준다.

 

 

src/docs/asciidoc 에 파일명.adoc을 작성한다.

 

 

ADOC 작성

test를 실행하고 나면 build 폴더 안에 Generated-snippets에 문서가 작성된다.

이런식으로 편리하게 작성된다.

728x90

'Dev > Spring' 카테고리의 다른 글

ResponseEntity  (0) 2021.08.25
@Transactional  (1) 2021.08.24
DAO,DTO,Entity Class  (0) 2021.08.13
@RequestMapping에 대하여  (0) 2021.07.24
[Spring]@RestController 와 @Controller의 차이  (0) 2021.07.18

DAO,DTO,Entity Class

ryudjae
|2021. 8. 13. 23:26
728x90

➡️DAO 란?

  • Data Access Object의 약자로 실제로 DB에 접근하는 객체이다.
  • 서비스와 DB를 연결하는 역할을 한다.
  • SQL을 사용하여 DB에 접근한 후 적절한 CRUD API를 제공한다.(ex:JPA)

➡️DTO 란?

  • Data Transfer Object 의 약자로 계층간 데이터 교환을 위한 객체이다.
    • DB에서 데이터를 얻어 서비스나 컨트롤러 등으로 보낼 때 사용하는 객체를 말한다.
    • 로직을 갖고 있지 않은 순수한 데이터 객체이며,getter/setter메서드만 갖는다.
    • 그러나 DB에서 꺼낸 값을 임의로 변경 할 필요가 없기 때문에 DTO클래스에는 setter가 없다 그대신 생성자에서 값을 할당한다.
  • Request와 Response용 DTO는 View를 위한 클래스이다.
    • 자주 변경이 필요한 클래스
    • toEntity메서드를 통해서 DTO에서 필요한 부분을 이용하여 Entity로 만든다.

➡️Entity Class 란?

  • 실제 DB 테이블과 매칭될 클래스이다.
    • @Entity,@Column,@Id 사용
  • 최대한 외부에서 Entity클래스의 getter method를 사용하지 않도록 해당 클래스 안에서 필요한 로직 메서드를 구현한다.
    • 도메인 로직만 가지고 있어야 하고 프레젠테이션 로직을 가지고 있으면 안된다.

 

➡️Entity Class 와 DTO를 분리하는 이유

  • View Layer과 DB Layer의 역할을 확실하게 분리하기 위해서 분리한다.
  • 테이블과 매핑되는 Entity클래스가 변경되면 여러 클래스에 영향을 끼친다, 그러나 View와 통신하는 DTO클래스는 자주 변경되므로 분리하여야 한다.
  • DTO는 도메인 모델을 복사한 형태로 다양한 프레젠테이션로직을 추가한 정도로 사용하며 도메인 모델 객체는 persistent만을 위해서 사용한다.

=>예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Getter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User extends BaseTimeEntity {
    @Id
    @GeneratedValue
    private Long id;
 
    private String email;
 
    private String password;
 
    private String nickname;
 
    private String phone;
cs

 


REFERENCE

728x90

'Dev > Spring' 카테고리의 다른 글

@Transactional  (1) 2021.08.24
Spring REST Docs  (0) 2021.08.23
@RequestMapping에 대하여  (0) 2021.07.24
[Spring]@RestController 와 @Controller의 차이  (0) 2021.07.18
로깅(Logging)  (0) 2021.07.13
728x90

 

 

요청이 들어오면 특정 메서드와 요청하기 위해 사용하는 것이 @RequestMapping이다.

@RequestMapping에서 가장 많이 사용하는 것은 valuemethod 이다.

  • value는 요청 받을 url을 설정할 수 있다.
  • method는 어떤 요청을 받을지 정의한다.(GET,POST,DELETE,PUT,PATCH 등 )

/v1 url로 매핑하는 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RestController
public class ControllerV1 {
 
    @RequestMapping(value = "/v1", method = RequestMethod.GET)
    public String getMapping(...) {
        ...
    }
 
    @RequestMapping(value = "/v1", method = RequestMethod.POST)
    public String postMapping(...) {
        ...
    }
 
    @RequestMapping(value = "/v1", method = RequestMethod.PUT)
    public String putMapping(...) {
        ...
    }
 
    @RequestMapping(value = "/v1", method = RequestMethod.DELETE)
    public String deleteMapping(...) {
        ...
    }
 
    @RequestMapping(value = "/v1", method = RequestMethod.PATCH)
    public String patchMapping(...) {
        ...
    }
}
cs

이렇게 /v1 GET,POST,DELETE,PUT,PATCH 으로  만들수 있다.

그러나 눈에 확 들어오지 않고 조금 반복적인 코드가 많이 발생한다.

조금 더 효율적이고 간단한 코드를 한번 보자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RestController
public class ControllerV1 {
 
    @GetMapping("/v1")
    public String getMapping(...) {
        ...
    }
 
    @PostMapping("/v1")
    public String postMapping(...) {
        ...
    }
 
    @PutMapping("/v1")
    public String putMapping(...) {
        ...
    }
 
    @DeleteMapping("/v1")
    public String deleteMapping(...) {
        ...
    }
 
    @PatchtMapping("/v1")
    public String patchMapping(...) {
        ...
    }
}
cs

이렇게 바꿀수 있다. 확실히 전 코드 보다는 간략하고 눈에 더 잘들어 온다.

 

그러나 여기서 보면  "/v1"이 중복되는걸 조금 수정하고 싶어진다.

클래스 레벨에서 중복되는 url주소를 적으면 중복되는 코드를 조금이나마 줄일수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
@RequestMapping("/v1")
public class ControllerV1 {
 
    @GetMapping("/cont")
    public String getMapping(...) {
        ...
    }
 
    @PostMapping
    public String postMapping(...) {
        ...
    }
 
    @PutMapping
    public String putMapping(...) {
        ...
    }
 
    @DeleteMapping
    public String deleteMapping(...) {
        ...
    }
 
    @PatchtMapping
    public String patchMapping(...) {
        ...
    }
}
cs

이런식으로 클래스 레벨에 등록하면 중복되는 url경로도 줄일 수 있다.

그리고 GetMapping에 보면 ("/cont")가 있다. 그렇다면 이 경로는 "/v1/cont"로 요청된다.

@RequestMapping은 클래스와 메소드에 붙일 수 있고, @GetMapiing,@PostMapping,@PutMapping,@DeleteMapping,@PatchMapping은 메소드에만 붙일 수 있고 클래스 레벨에는 붙일 수 없다.

 

 

이런식으로 어노테이션으로 중복되는 코드를 줄이고 효율적인 코드를 작성 할 수 있다.

 

728x90

'Dev > Spring' 카테고리의 다른 글

Spring REST Docs  (0) 2021.08.23
DAO,DTO,Entity Class  (0) 2021.08.13
[Spring]@RestController 와 @Controller의 차이  (0) 2021.07.18
로깅(Logging)  (0) 2021.07.13
[Spring]다양한 의존관계 주입 방법  (0) 2021.06.25
728x90

➡️Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있다.

전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차이점은 HTTP Response Body가 생성되는 방식에서 나타난다.

 


➡️@Controller

  • SpringMvc 컨트롤러인 @Controller는 주로 View를 반환하기 위해 사용한다.

 

  1. 클라이언트는 URI형식으로 웹 서비스애 요청을 보낸다.
  2. DispatcherServlet이 요청을 받아 매핑되는 핸들러와 그 type을 찾아 준다.
  3. 다음은 요청한 뷰 이름으로 viewResolver를 찾는다.
  4. viewResolver는 뷰의 논리 이름을 물리 이름으로 바꾸고 ,렌더링 역할을 담당하는 뷰 객체를 반환한다.
  5. 마지막으로 뷰를 통해서 뷰를 렌더링 한다.
  6. 렌더링 한 후에는 뷰를 사용자에게 반환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
@RequestMapping("/spring/members")
public class SpringMemberControllerV3 {
    
    @GetMapping
    public String members(Model model) {
 
          List<Member> members = memberRepository.findAll();
 
          model.addAttribute("members", members);
 
          return "/members";
    }
}
 
 
cs

@Controller를 위 코드처럼 사용한다면 DispatcherServlet이  핸들러 어댑터를 호출한다. 핸들어 어댑터를 통해 members라는  뷰 이름을 획득한다.그리고 뷰를 반환한다.

 

혹여나 @Controller로 데이터를 반환할려고 한다면 @ResponseBody를 사용해줘야 한다.


@RestController

  • RestController는 SpringMvc Controller에 @ResponseBody가 추가된 것이다.
  • RestController는 주로 Json 형태로 객체 데이터를 반환하는 것이다.
  • RestController는 viewResolver대신에 HttpMessageConverter가 동작한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
    
 
 
    /**
     * GET /mapping/users
     */
    @GetMapping
    public String users() {
        return "get users";
    }
 
 
    /**
     * POST /mapping/users
     */
    @PostMapping
    public String addUser() {
        return "post user";
    }
}
 
cs
  • @Controller 는 반환 값이 String 이면 뷰 이름으로 인식된다. 그래서 뷰를 찾고 뷰가 랜더링 된다.
  • @RestController 는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다. 따라서 실행 결과로 get users, post users 메세지를 받을 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class ResponseBodyController {
 
  
    @ResponseStatus(HttpStatus.OK)
    @GetMapping("/response-body-json")
    public MyData responseBodyJsonV2(){
        MyData myData = new MyData();
        myData.setUsername("KK");
        myData.setAge(22);
 
        return myData;
    }
 
}
 
cs
  • 이런식으로 데이터를 넣을때 사용이 많이 된다.
  • 이렇게 데이터를 넣으면  HTTP 메시지 바디에 Json형태로 {"username":"KK","age":"22"}로 반환이 된다.

➡️HttpMessageConverter 간단한 동작 방식

@RestController가 Data를 반환하기 위해서는 viewResolver 대신에 HttpMessageConverter가 동작합니다. HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 따라 사용되는 Converter가 달라진다.

단순 문자열인 경우에는 StringHttpMessageConverter가 사용되고, 객체인 경우에는 MappingJackson2HttpMessageConverter가 사용되며, 데이터 종류에 따라 서로 다른 MessageConverter가 작동하게 된다.

Spring은 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해 적합한 HttpMessageConverter를 선택하여 이를 처리한다. 

  • 대표적인 것들이 MappingJackson2HttpMessageConverter, StringHttpMessageConverter가 있다. StringHttpMessageConverter는 요청이 String으로 들어오면 문자로 처리한다.
  • MappingJackson2HttpMessageConverter는 요청이 객체 또는 HashMap으로 들어 오면 JSON으로 처리한다.

 

➡️정리

  • @Controller는 데이터를 처리 할 수 있지만 view만 처리하고 @RestController와 분리해서 @Controller는 뷰 처리를 하고 @RestController는 데이터 처리를 분리 해주는 것이 좋을거 같다.
  • 일반적은 Spring MVC @Controller와 RESTful 컨트롤러인 @RestController의 차이점은 HTTP Response Body가 생성되는 방식이다.
  • @Controller 는 View Page를 반환하지만, @RestController는 객체(VO,DTO)를 반환하기만 하면, 객체데이터는 application/json 형식의 HTTP ResponseBody에 직접 작성되게 된다.

 

 

728x90

'Dev > Spring' 카테고리의 다른 글

DAO,DTO,Entity Class  (0) 2021.08.13
@RequestMapping에 대하여  (0) 2021.07.24
로깅(Logging)  (0) 2021.07.13
[Spring]다양한 의존관계 주입 방법  (0) 2021.06.25
[Spring]의존성 주입(Dependency Injection)  (0) 2021.06.24

로깅(Logging)

ryudjae
|2021. 7. 13. 13:41
728x90

 

 

➡️ 로깅(logging)이란?

  • 정보를 제공하는 일련의 기록인 로그(log)를 생성하도록 시스템을 작성하는 활동이다.
  • 프린트 줄 넣기(printlning)는 간단한, 보통은 일시적인, 로그를 생성하기만 한다. 
  • 시스템 설계자들은 시스템의 복잡성 때문에 로그를 이해하고 사용해야 한다.
  • 로그가 제공하는 정보의 양은, 이상적으로는 프로그램이 실행되는 중에도, 설정 가능해야한다.
  • 일반적인 로그 기록의 이점
    • 로그는 재현하기 힘든 버그에 대한 유용한 정보를 제공할 수 있다.
    • 로그는 성능에 관한 통계와 정보를 제공할 수 있다.
    • 설정이 가능할 때, 로그는 예기치 못한 특정 문제들을 디버그하기 위해, 그 문제들을 처리하도록 코드를 수정하여 다시 적용하지 않아도, 일반적인 정보를 갈무리할 수 있게 한다.

➡️로그를 출력하는 방법

  • System.out.println()을 사용한다
  • 로깅 라이브러리를 사용한다.

로그 라이브러리는 Logback,Log4j,Log4J2등등 많은 라이브러리가 있는데,그것을 통합해서 제공하는 것이 바로 SLF4J 라이브러리이다.

SLF4J는 인터페이스고, 그 구현체로 Logback같은 로그 라이브러리를 선택하면 된다.

스프링 부트는 기본으로 SLF4J와 logback을 로깅 라이브러리를 사용한다.

 

➡️로그 사용법

로그 선언

1
2
3
private Logger log = LoggerFactory.getLogger(getClass());
 
private static final Logger log = LoggerFactory.getLogger(XXX.class);
cs

위 두가지 방법이 라이브러리를 쓸때 일반적인 방법이다. 

그러나 스프링부트를 사용하면서 롬복을 추가 하면 더욱더 간단하게 로그 선언을 할 수 있다.

롬복 로그선언 :@SLF4J

세 가지 방법 모두 다 동일하다.스프링 부트를 사용한다면 롬복으로 선언하는게 조금 더 편리해 보인다.


로그 호출

1
2
3
log.info("info log ={}",name);
 
System.out.println("info log ={}",name);
cs

위 두가지 방법이 있다.시스템 콘솔로 직접 출력하는거 보다 로그를 사용하는것이 좋다.

로그를 호출할 때 되도록이면 System.out.println은 사용을 하지 않는것이 좋다. 성능 차이가 너무 확연하게 난다.

 

간단한 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@Slf4j
public class LogTestController {
 
      @RequestMapping("/log-test")
      public String logTest() {
          String name = "Spring";
          log.trace("trace log={}", name);
          log.debug("debug log={}", name);
          log.info(" info log={}", name);
          log.warn(" warn log={}", name);
          log.error("error log={}", name);
 
 
        return "ok";
    } 
}
cs

이 처럼 사용하면 콘솔창에 로그가 찍힌다.

이렇게 보면 확실히 System.out.println보다 깔끔하고 정확하게 확인 할 수 있다.

로그가 출력되는 순서대로 시간, 로그,레벨, 프로세스 ID,쓰레드 명,클래스명,로그메시지이다.


로그 레벨

  • 로그를 출력할 때 로그 레벨을 설정해서 원하는 레벨까지의 로그만을 출력 할 수 있다.
  • 로그 레벨 : TRACE ->DEBUG ->INFO->WARN ->ERROR
  • 개발 서버에서는 DEBUG까지 출력해도 상관 없지만 운영 서버에서 DEBUG를 출력하면 너무 방대한 양의 로그가 출력되기 때문에 운영 서버에서는 INFO레벨로 출력해주는 것이 좋다.

로그 레벨 설정

 application.properties에서 설정 해준다.

#전체 로그 레벨 설정(기본 info)
logging.level.root=info
#hello.springmvc 패키지와 그 하위 로그 레벨 설정 
logging.level.hello.springmvc=debug


 

로그 사용시 주의할점

  • log.debug("data="+data) :로그 출력 레벨을 INFO로 설정해도 해당 코드에 있는 더하기 연산이 실제로 실행되어 출력은 되지 않지만 더하기 연산으로 인해 값은 저장되므로 메모리를 사용하고,CPU를 소모한다.
  • log.debug("data={}", data) :로그 출력 레벨을 info로 설정하면 아무일도 발생하지 않는다. 따라서 앞과 같은 의미없는 연산이 발생하지 않는다.

 

로그 사용시 장점

  • 쓰레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
  • 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.
  • 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다. 특히 파일로 남길 때는 일별,
  • 특정 용량에 따라 로그를 분할하는 것도 가능하다.
  • 성능도 일반 System.out보다 좋다. (내부 버퍼링, 멀티 쓰레드 등등) 

 

마무리

로그를 여기서 모두 설명하기엔 양이 너무 방대 하기 때문에 혹시 더 자세한 내용이 궁금하다면 직접 검색해보면서 공부하는것이 조금 더 효율 적이라고 생각한다.

SLF4J - http://www.slf4j.org
Logback - http://logback.qos.ch

 

728x90
728x90

 

 

의존관계 주입은 크게 4가지 방법이 있다.

  • 수정자 주입 (setter 주입)
  • 필드 주입
  • 일반 메서드 주입 
  • 생성자 주입 

 

👉수정자 주입(setter 주입)

  • setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.
  • 선택,변경 가능성이 있는 의존관계에 사용한다.
  • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class OrderServiceImpl implements OrderService{
 
    private MemberRepository memberRepository;
    private DisCountPolicy discountPolicy;
 
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository){
        
        this.memberReposityory = memberRepository;
    
    }
 
    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy){
        
        this.discountPolicy = discountPolicy;
        
    }
 
 
}
cs

 

👉필드 주입

  • 말 그대로 필드에 바로 주입하는 방법이다.
  • 코드가 간결해서 좋을거라고 생각할 수 있지만 외부에서 변경이 불가능 해서 테스트를 하기 힘들다는 치명적인 단점을 가지고 있다.
  • DI프레임워크가 없으면 아무것도 할 수 없다.
  • 필드 주입이 유용하게 쓰일수 있는 곳은 실제 코드와 관련이 없는 테스트 코드에서는 사용할 수 있다.
  • 스프링 설정을 목적으로 하는 @Configuration같은 곳에서만 특별한 용도로 사용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class OrderServiceImpl implements OrderService{
 
    @Autowired
    private MemberRepository memberRepository;
 
    @Autowired
    private DisCountPolicy discountPolicy;
 
    
 
 
}
cs

 

 

👉일반 메서드 주입

  • 일반 메서드를 통해서 주입 받을 수 있다.
  • 한번에 여러 필드를 주입 받을 수 있지만 잘 사용하지는 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class OrderServiceImpl implements OrderService{
 
 
    private MemberRepository memberRepository;
    private DisCountPolicy discountPolicy;
 
    @Autowired
    public void init(MemberRepository memberRepository,DisCountPolicy discountPolicy){
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;         
 
}
cs

 

👉생성자 주입

  • 이름 그대로 생성자를 통해서 주입 받는 방법이다.
  • 생성자 호출 시점에 딱 한번만 호출되는 것이 보장된다.
  • 불변,필수 의존관계에 사용 
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class OrderServiceImpl implements OrderService{
 
 
    private final MemberRepository memberRepository;
    private final DisCountPolicy discountPolicy;
 
    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository,DisCountPolicy discountPolicy){
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;         
 
}
cs

+@ 생성자가 하나만 있으면 @Autowired를 생략해고 자동 주입된다.

 

👉생성자 주입 권장

일단 대부분이 이 4가지 방법 중에 생성자 주입을 권장한다. 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다.

 

👉생성자 주입 권장하는 이유

불변

  • 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지의 의존관계를 변경할 일이 없다.
  • 그리고 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안된다.
  • 수정자 주입을 사용하면, setXXX메서드를 public으로 열어 두어야 한다.혹여나 누가 변경할 수 도 있기때문에 좋은 설계는 아니다.
  • 생성자 주입은 객체를 생성할 때 딱 한번만 호출되서 이후에는 호출될 일이 없다.

또 다른 장점은 생성자 주입을 사용하면 주입데이터를 누락 했을 때 컴파일 오류가 발생한다.그래서 바로 알 수 있다는 장점이 있다.

생성자 주입시에 final키워드를 사용한다.생성자에 혹시라도 설정되지 않는 오류를 컴파일 시점에서 막아준다.

 

 

 

정리

  • 생성자 주입 방식은 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘살리는 방법이기도 하다.
  • 기본적으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다.
  • 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
  • 필드 주입은 되도록이면 사용하지 않는게 좋다.
728x90

'Dev > Spring' 카테고리의 다른 글

@RequestMapping에 대하여  (0) 2021.07.24
[Spring]@RestController 와 @Controller의 차이  (0) 2021.07.18
로깅(Logging)  (0) 2021.07.13
[Spring]의존성 주입(Dependency Injection)  (0) 2021.06.24
[Spring]@RequiredArgsConstructor  (0) 2021.06.23
728x90

➡️의존성 주입이란

  • 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법이다.
  • 예를 들어 장난감,리모컨,휴대폰 모두 배터리를 교체 할 수 있는 제품이라면 장난감,리모컨,휴대폰 모두 배터리에 의존하기 때문에 배터리를 갈아끼우는 것을 의존성 주입이라고 보면 된다.
  • 그러나 만약 장난감이나 휴대폰이 배터리 교체가 불가능한 일체형이라면 교체하지 못하고 새제품으로 바꿔야 하기때문에 설계로 본다면 유연하지 못한 설계가 된다.

밑에 예제를 보고 의존성 주입이 필요한 이유를 알아보자

1
2
3
public class Americano{
 
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
public class Cafe{
 
    private Americano americano; 
    
    public Cafe() { 
 
        this.americano = new Americano();
    }
 
 
 
}
cs
  • 예를 들어 아메리카노를 파는 카페 클래스를 만들었다.
  • 그러나 위 예제는 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어지고 있다. 객체지향적 설계라면 객체들 간에 관계가 맺어져야 하지만 현재는 카페 클래스와 아메리카노 클래스끼리 관계를 맺고 있다.객체들 간에 관계가 맺어졌다면 다른 커피종류의 클래스가 생겼는데 카페클래스가 알지 못하더라도 인터페이스 타입으로 사용할 수 있다.
  • 그리고 카페클래스에서 메뉴를 추가할려면 카페클래스의 생성자 변경이 필요하다.일단 유연성이 떨어진다는 단점이 있다.
  • 이걸 보면 상속으로는 해결할 수 있을거 같은데?라고 생각 하실수도 있지만 상속은 제약이 많고 확장성이 떨어지므로 피하는것이 좋다.
  • 이 같은 문제는 관심사 분리가 되지 않았기 때문에 발생하는 문제이다. Spring에서는 의존성 주입을 통하여 이런 문제를 해결하려고 한다.

 

위와 같은 문제를 해결하기 위해선 다형성이 필요하다.일단 여러 메뉴를 하나로 표현하기 위해서는 인터페이스가 필요하다.그리고 Americano클래스에서 인터페이스를 우선 구현을 해준다.

1
2
3
4
public interface Menu{
 
 
}
cs

 

1
2
3
4
public class Americano implements Menu{
 
 
}
cs

 

다음으로 위에 문제가 있는 코드에서 Cafe와 Americano가 결합된 부분을 제거해준 다음 외부에서 상품을 주입(Injection)받아야 한다

1
2
3
4
5
6
7
8
9
10
11
public class Cafe { 
    
    private Menu menu; 
    
    public Cafe(Menu menu) { 
           
  this.Menu = menu;
 
    } 
}
 cs
 
   

➡️이제 여기서부터 Spring이 DI컨테이너가 필요하다. 왜냐하면 Cafe에서 Menu객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체를 생성해야 하고,의존성이 있는 두 객체를 연결하기 위해 한객체는 다른 객체주입시켜야 하기 떄문이다.

1
2
3
4
5
6
7
8
9
10
11
12
public class BeanFactory { 
    public void cafe() {
 
            Menu americano = new Americano(); 
            
 
            Cafe cafe = new Cafe(americano); 
    }
 
}
 
 



cs

➡️위 처럼 Americano객체를 만들고 ,객체를 Cafe로 주입시켜주는 역할을 위해서 DI 컨테이너가 필요한것이다.

 

➡️또 이러한 개념은 제어의 역전이라고 불리기도 한다.객체를 사용할지에 대한 책임이 Beanfactory로 넘어갔고 객체 자신은 수동적으로 중비받는 객체를 사용하기때문이다.

 

💻의존성 주입 장단점

장점

  •  재사용성을 높여준다.
  •  테스트에 용이하다.
  • 코드를 단순화 시켜준다.
  • 사용하는 이유를 파악하기 수월하고 코드가 읽기 쉬워지는 점이 있다.
  • 종속성이 감소하기 때문에 변경에 민감하지 않다.
  • 결합도(coupling)는 낮추면서 유연성과 확장성은 향상 시킬 수 있다.
  • 객체간의 의존관계를 설정할 수 있다.

단점

  • 의존성 주입을 위한 선행작업이 필요함.
  • 코드를 추적하고 읽기가 어려워짐.

 

마무리

➡️한 객체가 어떤 객체를 의존할 것인지는 별도의 관심사다.DI 컨테이너를 통해 서로 강하게 결합되어 있는 두 클래스를 분리하고, 두 객체 간의 관계를 결정해 줌으로써 결합도를 낮추고 유연성을 확보하고자 하였다. 의존성 주입으로 애플리케이션 실행 시점에 객체를 생성하고 관계를 결정해 줌으로써 다른 구체 클래스에 의존하는 코드를 제거하며 서로 다른 두 객체의 결합을 약하게 만들어 주었다. 또한 이러한 방법은 상속보다 훨씬 유연하다
의존 관계를 주입할 객체를 계속해서 생성하고 소멸한다면, 아무리 GC가 성능이 좋아졌다고 하더라도 부담이 된다. 그래서 Spring에서는 Bean들을 기본적으로 싱글톤(Singleton)으로 관리하는게 좋을거 같다.

 

 


REFERENCE 

 

728x90

'Dev > Spring' 카테고리의 다른 글

@RequestMapping에 대하여  (0) 2021.07.24
[Spring]@RestController 와 @Controller의 차이  (0) 2021.07.18
로깅(Logging)  (0) 2021.07.13
[Spring]다양한 의존관계 주입 방법  (0) 2021.06.25
[Spring]@RequiredArgsConstructor  (0) 2021.06.23