관리 메뉴

JIE0025

[Spring] Annotation 본문

백엔드/스프링, 스프링부트, JPA, Spring Webflux

[Spring] Annotation

Kangjieun11 2022. 11. 24. 01:02
728x90


개인적으로 공부하면서 정리한 내용입니다 : )




FLAG Project - BE Study 

2022.11.10





FLAG








Annotation

  • Annotation
  • Spring Annotation
    • 주요 Annotation
    • Spring boot Annotation
    • Spring MVC Annotation
    • JPA Annotation
    • JUnit Annotation


Annotation

⏺ 어노테이션 용도

- 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보 제공
- SW개발 툴이 빌드/배치시 코드를 자동 생성할 수 있도록 정보 제공
- 런타임시 특정 기능을 실행하도록 정보 제공


스프링 주요 어노테이션

스프링 MVC Annotation



⬛️ Config

@Configuration

스프링 설정 정보로 인식

  • 어노테이션 기반 환경 구성, 빈 설정을 담당하는 클래스가 됨
  • 이 클래스 안에서 @Bean어노테이션 메서드 선언시 메서드를 통해 스프링 빈을 정의하고 생명주기를 설정하게 됨.
    •  스프링 빈이 싱글톤을 유지하도록 추가 처리를 함(스프링 빈 스코프가 싱글톤이 아니면 추가처리 안함)

@Bean

컨테이너에 빈을 등록
개발자가 컨트롤이 불가능한 외부 라이브러리들을 빈으로 등록하고 싶을 때 사용함.

메소드/어노테이션 단위에 붙일 수 있다.

@Component

스프링 컨테이너에 빈을 알아서 등록하게 만듬
컴포넌트 스캔 : @Component를 명시해 빈을 추가하는 방법
컴포넌트 스캔 대상 : @Controller, @Service, @Repository, @Conficguration (@Component의 상속을 받고 있어서)


* @ComponentScan("com.sdoaod.web.controller")
--- 나중에 봐야지 https://oingdaddy.tistory.com/254




어노테이션은 아니지만 같이 보면 좋을것 같아 함께 추가

⬛️ DTO

DTO(Data Transfer Object)는 계층 간 데이터 교환을 하기 위해 사용하는 객체,
DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)

  • 유저가 자신의 브라우저에서 데이터를 입력하여 form에 있는 데이터를 DTO에 넣어서 전송한다.
  • 해당 DTO를 받은 서버가 DAO를 이용하여 데이터베이스로 데이터를 집어넣는다.


https://melonicedlatte.com/2021/07/24/231500.html



⬛️ repository

@Repository

스프링 데이터 접근 계층으로 인식, 해당 계층에서 발생하는 예외는 모두 DataAccessException으로 변환함.
→ 나중에 ComponentScan으로 찾아서 Bean으로 등록

@Repository
public class UserRepoImpl implements UserRepository {
 
    // ...
 
}


⬛️ Service

@Service

특별한 처리는 하지 않지만, 핵심 비지니스 계층 인식에 도움을 줌
Bean으로 관리되기 위해 명시

@Autowired

스프링 빈을 가져오는 가장 기본적인 방법

  • IoC컨테이너안에 존재하는 Bean을 자동으로 주입한다.
  • 필드, 메서드, 생성자에 추가 가능하다.
@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserRepository userRepo;
 
    // ..
 
}

빈으로 UserRepository가 있을 때, 자동적으로 가져와서 주입해 종속된다.

@Qualifier

동일한 타입의 Bean객체 두개가 있을 때 해당 Bean id값을 지정해 구분가능하게 한다.
@Qualifier("해당id") 를 설정, @Autowired와 함께 선언


⬛️ Controller

@Controller

스프링 MVC 컨트롤러 인식
Bean으로 관리되기 위해 명시

@RequestMapping()

method에 명시된 방식으로 value path를 요청하는 경우

  • value : 요청받은 url 설정
  • method : 어떤 방식으로 받을지 정의 (GET, POST, PUT, DELETE)
	@RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
                
        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
        
        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );
        
        return "home"; // return : logical view name
    }


불편하다.

@RestController
public class HelloController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String helloGet(...) { ... }

    @RequestMapping(value = "/hello", method = RequestMethod.POST)
    public String helloPost(...) { ... }

    @RequestMapping(value = "/hello", method = RequestMethod.PUT)
    public String helloPut(...) { ... }

    @RequestMapping(value = "/hello", method = RequestMethod.DELETE)
    public String helloDelete(...) { ... }
}

--------------개선

@RestController
@RequestMapping(value = "/hello")
public class HelloController {

    @GetMapping()
    public String helloGet(...) { ... }

    @PostMapping()
    public String helloPost(...) { ... }

    @PutMapping()
    public String helloPut(...) { ... }

    @DeleteMapping()
    public String helloDelete(...) { ... }
}

@RequestMapping을 클래스에 설정하고, 새로운 어노테이션 사용
추가 url을 붙히고 싶을 때 @GetMapping("/hihi")이런식으로 넣으면 됨.

* 당연한 말이지만
@RequestMapping 은 클래스, 메서드에 붙힐수있고
@GetMaping들은 메서드에만 붙힐수있다.



⬛️ 서버-클라이언트 통신 관련 (?비동기랑만 관련있는지 잘 모르겠음?)

@ResponseBody

비동기통신을 하는 상황에서
클라이언트에서 서버로 요청 메세지를 보낼 때, 본문에 데이터를 담아서 보내야 하고,
서버에서 클라이언트로 응답을 보낼때에도 본문에 데이터를 담아서 보내야 한다.

이 본문이 바로 body
즉, 요청본문 requestBody, 응답본문 responseBody 을 담아서 보내야 한다.
대표적으로 JSON형식으로 데이터를 주고받는다.

@RestController

@Controller와 다르게, @RestController는 리턴값에 자동으로 @ResponseBody가 붙게된다
별도 어노테이션을 명시해주지 않아도 HTTP 응답데이터(body)에 자바 객체가 매핑되어 전달 됨

*********
@Controller를 사용하면 바디를 자바 객체로 받기 위해서
@ResponseBody 어노테이션을 반드시 명시해주어야함.
*********

클라이언트에서 서버로 요청 메세지를 보낼 때 Json data를 요청 본문에 담아서 서버로 보내는데 다음과 같은 두 방식이 사용된다.

@RequestBody , @RequestParam

@RequestParam : url상에서 데이터를 전달하는 경우(form 태그 등)
@RequestBody : 그 외의 경우에 사용

@PathVairable

url경로에 변수를 넣어주는 것.

  • @RequestMapping 어노테이션 값으로 {템플릿변수} 를 사용
  • @PathVariable 어노테이션을 이용해서 {템플릿 변수} 와 동일한 이름을 갖는 파라미터를 추가하면 됨
  • RequestMapping 어노테이션에 변수를 포함하고 있음!
@RequestMapping(value = "/user/email/{email}", method=RequestMethod.GET)

이 형식에서 아래처럼 쓸 수 있다. (사실 안써봐서 잘모르겠다 직접 써보면서 익혀야할것 같다.)

@RequestMapping(value = "user/email/{email:.+}", method = RequestMethod.GET)
public ModelAndView getUserByEmail(@PathVariable("email") String email) {

  • null, 공백값 들어가는 파라미터는 적용하지 말기
  • Spring 에서 @PathVariable 사용하여 값을 넘겨받을때 값에 . 가 포함되어 있으면 .포함하여 그뒤가 잘려서 들어온다는 것!
RequestParam PathVariable
http://192.168.0.1:8080?aaa=bbb&ccc=ddd http://192.168.0.1:8080/bbb/ddd

 


너무 많아서,ㅡ 아래는 나중에 할란다 ㅠㅠ

더보기

@PropertySource

@CookieValue

@Lazy

@Value

@Required


JPA Annotation

javax.persistence 패키지 않의 수많은 어노테이션 인터페이스들이 존재


@Entity

클래스가 DB 테이블과 1:1 매칭

  • 기본 생성자가 꼭 필요
  • final, enum, interface, innter class에서는 사용 불가
  • 필드(변수)를 final로 선언 불가

아래 예제는 user2 테이블이 생성된다.

@Entity(name = "user2")
public class User {}

@Table(name="??")

엔티티와 매핑할 테이블 지정

속성 기능
name 매핑할 테이블 이름
생략시 엔티티 이름(@Entity(name="~") 사용
catalog catalog 기능이 있는 DB에서 catalog 매핑
schema schema기능이 있는 DB에서 schema 매핑
uniqueContraints DDL 생성시 유니크 제약조건 생성
※ 스키마 자동 생성 기능을 사용해 DDL을 만들 때만 사용

@Table(name= "user3")
name속성 추가시 테이블이름이 name값으로 설정, 생략시 Entity이름으로 테이블이 만들어짐


@Column

객체 필드를 테이블 컬럼과 매핑
//옵션 왕많아서 그냥 생략함 검색하면서 써야겠다.

@Id

멤버변수가 테이블의 Primary key가 됨

@GeneratedValue(stargety=GenerationType.AUTO)

옵션에 따라 JPA 구현체가 자동으로 생성 전략을 결정

  • AUTO (default) : JPA 구현체가 자동으로 생성 전략을 결정한다.
  • IDENTITY : 기본키 생성을 데이터베이스에 위임한다.
    ex) MySQL- AUTOINCREMENT를 사용해 기본키를 생성함.
  • SEQUENCE : 데이터베이스의 특별한 오브젝트 시퀀스를 사용해 기본키를 생성한다.
  • TABLE : 데이터베이스에 키 생성 전용 테이블을 하나 만들고 이를 사용해 기본키를 생성한다.


@Access

JPA가 엔티티 데이터에 접근하는 방식을 지정함.

접근 방식 기능
AccessType.FILED 필드에 직접 접근
필드 접근 권한이 private여도 접근 가능
AccessType.PROPERTY getter를 통해 접근


@Enumerated(EnumType.STRING)

자바 enum타입 매핑시 사용

value EnumType.ORDINAL : enum 순서를 DB에 저장
EnumType.STRING : enum이름을 DB에 저장

@Temporal

날짜 타입 매핑시 사용

value TemporalType.DATE : 날짜, DB date 타입과 매핑(예 : 2020-02-12)
TemporalType.TIME : 시간, DB time 타입과 매핑(예: 12:12:12)
TemporalType.TIMESTAMP : 날짜와 시간 DB timestamp타입과 매핑(예 : 2020-02-12 12:12:12)

@Transient

DB에 저장도 조회도 안하고, 임시로 객체에 값을 보관할 때 사용

더보기

나..중..

@JoinColumn

Join할 컬럼 지정

@JoinTable

N:N 조인에 해당하는 테이블 선언

@Getter
@Setter


@NoArgsConstructor
@AllArgsConstructor


@ToString
@JsonNaming

@Data

@Builder
@AllArgsConstructor
@NoArgsConstructor

@Transactional(readOnly = true)

@Modifying

@Query(value = "truncate table reply", nativeQuery = true)

@RequiredArgsConstructor



JUnit Annotation

@SpringBootTest

  • 통합 테스트를 위한 환경을 준비
  • 모든 빈들을 스캔하고 애플리케이션 컨텍스트를 생성하여 테스트를 실행

@Transactional

Transaction을 begin하고 commit까지 시켜주는 어노테이션
Exception 발생시 Rollback까지 시켜줌

@AutoConfigureMockMvc

  • Controller의 API를 테스트하는 용도인 MockMvc 객체를 주입 받음
  • Perform()메소드를 활용하여 컨트롤러의 동작을 확인할 수있음
  • andExpect(), andDo(), andReturn() 등의 메소드를 같이 활용함

@WithMockUser

spring security에서 제공하는 어노테이션- 인증이 된 상태로 테스트를 진행하도록 도와줌
*SecurityconotextHolder에 UsernamePasswordAuthenticationToken이 담긴 상태를 만들어준다.

[ @Autowired와 @MockBean, @SpyBean ]
스프링부트가 제공하는 테스트 어노테이션들은 실제 스프링 컨텍스트를 사용해 빈들을 등록한다. 그러므로 @Autowired로 등록된 빈을 주입받을 수 있다. 특정 객체를 모킹하고 싶은 경우에는 @MockBean과 @SpyBean를 사용하면 된다.
@MockBean: 해당 객체를 Mock된 빈으로 등록함@SpyBean: 해당 객체를 Spy된 빈으로 등록함


@Mock

Mock 객체에 대해 더더 알아보고 추가하자.
Mockito에서 Mock 객체를 제공해준다고 위에서 언급이 되었는데, Mock 객체란 다음과 같다. Mock 객체란 개발한 프로그램을 테스트할 때 테스트를 수행할 모듈과 연결되는 외부의 다른 모듈을 흉내 내는 가짜 모듈을 생성하여 테스트의 효율성을 높이는 데 사용하는 객체

@MockBean

  • 테스트할 클래스에서 주입 받고 있는 객체에 대해 가짜 객체를 생성해주는 어노테이션
  • 해당 객체는 실제 행위를 하지 않음
  • given() 메소드를 활용하여 가짜 객체의 동작에 대해 정의하여 사용할 수 있음

@BeforeEach

각 테스트 함수가 불리기 전에 매번 호출

@Test

테스트 대상 메소드 지정

@Test(timeout=5000)
public void testSum(){

}

@DisplayName("이름!")

테스트 이름 표시해 가독성 높임



@Before

선언시 해당 메서드는 @Test 메소드가 실행되기 전 실행됨
@Test메서드에서 공통으로 사용하는 코드를 @Before 메서드에 선언하여 사용

@After

선언시 해당 메서드는 @Test 메소드가 실행된 후 실행됨



아래 어노테이션은 해당 테스트 클래스에서 전/후에, 딱 한번씩 수행되도록 지정된다.

@BeforeClass

@BeforeClass 어노테이션은 @Test 메소드보다 먼저 한번만 수행되어야 할 경우에 사용

@AfterClass

@AfterClass 어노테이션은 @Test 메소드 보다 나중에 한번만 수행되어야 할 경우에 사용





references

더보기

https://iammert.medium.com/annotation-processing-dont-repeat-yourself-generate-your-code-8425e60c6657

https://www.informit.com/articles/article.aspx?p=2027052&seqNum=6

https://www.semanticscholar.org/paper/Spoon%3A-Compile-time-Annotation-Processing-for-Pawlak/59543423dffb2ec53b72caa500db7d83b2c248a8/figure/1

https://www.happykoo.net/@happykoo/posts/256

https://www.charlezz.com/?p=1167

https://www.youtube.com/watch?v=o6-gRZfQqOE

https://cjw-awdsd.tistory.com/46

https://zepettoworld.tistory.com/4

1) form 태그를 이용해 요청메시지 전송시

@RequestBody → String 데이터로 전달됨

@RequestParam 데이터를 저장하는 이름으로 메서드의 변수명을 설정

2) json 형식으로 데이터 전송시

//@RequestParamurl 상에서 데이터를 찾음

http://localhost:8080/receive?name=jun&age=13

우리가 위에서 <form> 태그를 이용하여 데이터를 입력하고 제출 버튼을 누르면 입력한 데이터들이 url을 통해서 전달된다.

json형식으로 전송시 url은 변함이 없고 body에 데이터를 포함해 전송하기 때문에 @RequestParam으론 받을 수 없음.

https://ocblog.tistory.com/49

@Controller
public class UserController {

	@PostMapping("/receive")
	public String age(@RequestBody String req) {
		System.out.println("통신 성공");
		System.out.println(">>> " + req);
		return "result";
	}
}

//출력 결과
//통신 성공
//>>> name=jun&age=13

--------------------------

@Controller
public class UserController {

	@PostMapping("/receive")
	public String age(@RequestParam String name) {
		System.out.println("통신 성공");
		System.out.println(">>> " + name);
		return "result";
	}
}

//출력 결과
//통신 성공
//>>> jun