스프링(Spring) 어노테이션들 정리 + ResponseEntity
📌스프링 기본 어노테이션
1️⃣ @Target
@Target 이 선언된 하위 어노테이션이 어떤 범위에 적용되는지 설정한다.
@Target의 ElementType.ANNOTATION_TYPE는 아래 enum상수에 따라 범위를 결정한다
2️⃣@Component
Spring Bean에 등록하는 역할을 수행한다.
3️⃣@Retention
@Retention 하위의 어노테이션이 얼마나 오래 유지되는지를 결정한다.
4️⃣ @Document
Javadoc 등의 문서화 도구에 의해 문서화되어야 함을 나타낸다.
5️⃣ @Indexed
클래스가 컴포넌트 스캔의 대상으로 Spring Bean에 더 빠르게 등록되도록 도와준다.
6️⃣ @AliasFor
같은 어노테이션의 속성들끼리 동일한 값을 공유하도록 설정할 수 있음.
@Component
public @interface MyComponent {
@AliasFor("value") // 'name'이 'value'와 동일한 역할을 하도록 설정
String name() default "";
@AliasFor("name") // 'value'가 'name'과 동일한 역할을 하도록 설정
String value() default "";
}
하나의 커스텀 어노테이션이 다른 어노테이션을 감싸고 있을 때, 속성을 매핑하여 일관성 유지.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component // Spring의 @Component 기능을 포함
public @interface MyService { //커스텀 컴포넌트 느낌
@AliasFor(annotation = Component.class, attribute = "value") // @Component(value="myService") 와 동일한 효과
String value() default "";
}
@MyService("myServiceBean") // @Component("myServiceBean")과 동일한 효과
public class MyServiceBean {}
📌비슷한 어노테이션들
서로 특별한 기능이 있는것이 아니라 사실상 같다 볼 수 있지만 스프링이 역할 구분을 위해 @component를 확장한 것이다
@Component | 기본적인 스프링 빈 등록 |
@Controller | Spring MVC 컨트롤러 역할 (추가 기능 없음) |
@Service | 비즈니스 로직 담당 클래스 표시 (추가 기능 없음) |
@Repository | DAO(데이터 액세스) 담당 클래스 표시 + 예외 변환 기능 있음 |
🚨 차이점
- @Controller와 @Service는 완전히 같은 기능.
- @Repository만 JPA 같은 DB 연동 시 예외 변환(AOP 기반 트랜잭션 처리) 기능이 추가됨.
📌로그
▪️로그 순서
Log Level : TRACE > DEBUG > INFO > WARN > ERROR
▪️작성법
log.info("문자 trace={}", sparta);// 올바른 작성 방법
log.info("문자 info " + sparta); // 문자 연산을 먼저 해버린다.
▪️ application.properties에 아래 내용 추가하여 Log Level 순서를 지정가능 아래로 예시들면 TRACE 부터 시작
# com.example.springbasicannotation 하위 경로들의 로그 레벨을 설정한다.
logging.level.com.example.springbasicannotation=TRACE
▪️ 위의 내용을 주석하고 아래를 실행하면 기본값은 info부터 시작이기 떄문에 info,warn,error 다시 info 출력 후 끝난다
즉 위에 trace debug는 무시 한다
// TRACE -> DEBUG -> INFO -> WARN -> ERROR
log.trace("문자 trace={}", sparta);//실행 x
log.debug("문자 debug={}", sparta);//실행 x
// default
log.info("문자 info={}", sparta);// 문자 연산을 진행하지 않는다.
log.warn("문자 warn={}", sparta);
log.error("문자 error={}", sparta);
📌컨트롤러
◾Controller VS RestController
▪️Controller
@Controller
public class ViewController {
@RequestMapping("/view")
public String example() {
// logic
return "sparta"; // ViewName이 return
}
}
▪️특징
▪️ View를 반환 위에 코드를 예시로 들면 "sparta"를 문자열로 인식 x 👉 ThymeleafViewResolver 에 의해 View Name으로 인식된다. 즉 /sparta로 이동한다
👇 resources/templates/sparta.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<h2>Thymeleaf Template Sample</h2>
</body>
</html>
▪️RestController
@RestController
public class ResponseController {
@RequestMapping("/string")
public String example() {
// logic
return "sparta"; // ViewName이 return 되는게 아니라, String Data가 반환된다.
}
}
▪️특징
▪️View가아닌 데이터를 리턴 해줄 경우에 사용 return 값으로 View를 찾는것이 아니라 HTTP Message Body에 Data를 입력한다
❌ 차이점
둘의 차이는 @ResponseBody가 있고 없고에 따라 뷰 페이지 (ViewResolver가 처리) ,데이터(JSON, XML)를 다룬 다는점
📖 추가
@RestController를 보면 @Controller과 @ResponseBody 어노테이션을 포함 하는데 @RestController는 @Controller, @ResponseBody 의 역할도 하면서 추가적인 기능이 있다고 보면 된다
그리고 중복선언되는 어노테이션들은 @RestController의 어노테이션이 적용된다
즉 @ResponseBody는 @Target{ElementType.TYPE, ElementType.METHOD} 이지만
@RestController의 @Target{ElementType.TYPE}이 적용 상속받고 재정의 느낌이 있다
📌Mapping
◾@RequestMapping
특정 URL로 Request를 보내면 들어온 요청을 Controller 내부의 특정 Method와 Mapping 하기 위해 사용한다.
◾특징
- Spring Boot 3.0 버전 이하
- URL path /example, /example**/** 모두 허용(Mapping)한다.
- Spring Boot 3.0 버전 이상(현재 버전)
- URL path /example 만 허용(Mapping)한다.
- 속성값들을 설정할 때 배열 형태로 다중 설정이 가능하다
ex) @RequestMapping**({**”/example”, “/example2”, “/example3”**}) - HTTP Method POST, GET, PUT, PATCH, DELETE, HEAD 모두 허용한다
- method 속성으로 HTTP 메서드를 지정하면 지정된것만 허용한다.
ex) // HTTP Method 는 GET만 허용한다. @RequestMapping(value = "/v1", method = RequestMethod.GET)
◾@GetMapping
내부적으로 @RequestMapping(method = RequestMethod.GET) 을 사용하고 있다.
◾@PostMapping
내부적으로 @RequestMapping(method = RequestMethod.Post) 을 사용하고 있다.
📖 정리
RequestMapping는 클래스 레벨에도 적용이가능 하고 Get,Post등등 다른 메소드는
메소드 레벨만 적용가능 대신 축약,직관적으로 사용하고 싶을때 사용 즉 둘을 같이 사용하면
세분화 가능하다. 계층구조로 표현
◾활용 예시
@RequestMapping("/prefix")
@RestController
public class RequestMappingController {
// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v3")
public String exampleV3() {
// logic
return "this is sparta!";
}
}
👉 /prefix/...로 입력된 요청들을 다 처리한다 즉 /prefix/v3 or /prefix/v4 등등 처리 가능
📖 속성 설정을 통하여 특정 헤더, 특정 파라미터와 Mapping 하는 법
1️⃣ params 속성
// parms 속성값 추가
@GetMapping(value = "/users", params = "gender=man")
http://localhost:8080/users?gender=man 요청 시 호출 gender=man가 있아야 함 없으면 호출 x
◾규칙
- params = "gender"
- params의 key값은 커스텀이 가능하다
- value는 없어도 된다.
- params = "!gender"
- gender가 없어야 한다.
- params = "gender=man"
- gender=man 이어야 한다.
- params = "gender!=man"
- params의 value값이 man가 아니여야 한다.
- params = {"gender=man", "gender=woman"}
- 배열로 속성 값을 여러 개 설정이 가능하다.
2️⃣ 특정 Header 매핑
@PostMapping(value = "/users", headers = "Content-Type=application/json")
Content-Typer가 application/json인 경우만 호출
3️⃣ MediaType 매핑, consume(수용)
//consumes 속성값 추가
@PostMapping(value = "/users", consumes = "application/json") // MediaType.APPLICATION_JSON_VALUE
//위랑 같은 동작
@PostMapping(value = "/users", consumes = MediaType.APPLICATION_JSON_VALUE)
4️⃣ MediaType 매핑 produces(제공)
요청 헤더의 Accept 값에 따라서 produces 하는 값이 변한다.
consumes 처럼 Media. 으로 가져와서 사용가능
@GetMapping(value = "/users", produces = "text/plain")
@GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
📌 "클라이언트" 에서 온 데이터를 "서버" 에서 받을 때 처리
1️⃣ GET, form 데이터 형식으로 왔을 때(Query Param)
1️⃣ @PathVariable
- 경로 변수를 중괄호에 둘러싸인 값으로 사용할 수 있다.ex) user/{id}
- 기본적으로 @PathVariable로 설정된 경로 변수는 반드시 값을 가져야 하며 값이 없으면 응답 상태코드 404 Not Found Error가 발생한다.
◾ @PathVariable 규칙
파라미터 변수명과 PathVariable 변수명이 같으면 속성 값 생략 가능
@RequestMapping("/posts")
@RestController
public class PathVariableController {
// postId로 된 post 단건 조회
@GetMapping("/{postId}")
public String pathVariableV1(@PathVariable("postId") Long data) {
// logic
String result = "PathvariableV1 결과입니다 : " + data;
return result;
}
}
▪️생략
@RequestMapping("/posts")
@RestController
public class PathVariableController {
// 변수명과 같다면 속성값 생략가능
@GetMapping("/{postId}")
public String pathVariableV2(@PathVariable Long postId) {
// logic
String result = "PathvariableV2 결과입니다 : " + postId;
return result;
}
}
▪️ 클래스에도 사용가능
@RequestMapping("/posts/{postId}")
@RestController
public class PathVariableController {
@GetMapping("/comments/{commentId}")
public String pathVariableV4(
@PathVariable Long postId,
@PathVariable Long commentId
) {
// logic
String result = "PathvariableV4 결과입니다 postId : " + postId + "commentsId : " + commentId;
return result;
}
}
2️⃣@RequestParam
URL에서 파라미터 값과 이름을 함께 전달하는 방식으로 주로 HTTP 통신 Method 중 GET 방식의 통신을 할 때 많이 사용한다. @Requestparam을 사용하면 요청 파라미터 값에 아주 쉽고 간편하게 접근(Parameter Binding)할 수 있다.
@Controller
public class RequestParamControllerV2 {
@ResponseBody
@GetMapping("/v1/request-param")
public String requestParamV1 (
@RequestParam("name") String userName,
@RequestParam("age") int userAge
) {
// logic
log.info("name={}", userName);
log.info("age={}", userAge);
return "success";
}
}
- @Controller + @ResponseBody
- View를 찾는 것이 아니라 ResponseBody에 응답을 작성한다(=@RestController)
- @RequestParam
- 파라미터 이름으로 바인딩한다.
- @RequestParam(”속성값”)
- 속성값이 파라미터 이름으로 매핑된다.
- “속성값”과 변수명이 같으면 생략이 가능하다.
required는 파라미터의 필수 값을 설정한다. true면 값이 없으면 400 에러
String 경우 ""도 true로 봐서 조심 - 단순 타입(int, String, Integer 등)이어야 한다.
- default 속성 적용
기본값 설정
@RequestParam(required = true, defaultValue = "sparta") String name,
@RequestParam(required = false, defaultValue = "1") int age
String의 경우 ""도 default 값으로 바꿔줌
3️⃣ @ModelAttribute
요청 파라미터를 받아 필요한 Object로 바인딩 해준다. 주로 HTML 폼에서 전송된 데이터를 바인딩하고 HTTP Method POST인 경우 사용된다.
◾@ModelAttirubte 동작 순서
- 파라미터에 @ModelAttribute가 있으면 파라미터인 객체를 생성한다.
- 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩한다.
- 파라미터 이름이 name 이면 setName(value); 메서드를 호출한다. 즉 setter 필요
- 파라미터 이름과 필드 이름이 반드시 같아야 한다!
- 입력값이 맞지 않는 경우 오류나서 검증필요
📖GET vs form data 차이
둘다 서버로 왔을 떄는 Query Strimg 형식으로 같지만 보낼 때 차이가 있다
◾GET 👉 /user?name=이름&age=25
◾form data 👇
<form action="/user" method="post">
<input type="text" name="name" value="이름">
<input type="number" name="age" value="25">
<button type="submit">전송</button>
</form>
//서버에선
POST /user HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=이름&age=25
👉 form 형식이 보안에 좋아서 중요한 데이터는 form 간단한건 get 사용한다
📌 "클라이언트" 에서 온 데이터를 "서버" 에서 받을 때 처리
2️⃣ Json, XML, TEXT 형식, REST API에서 POST, PUT 요청을 처리할 때
1️⃣ @RequestBody
클라이언트가 보낸 HTTP 요청의 body를 Java 객체로 변환하는 역할 HttpMessageConverterrk 가 해줌
컨트롤러 리턴값을 view이동이아닌 데이터 전달로 사용하고 싶을 떄 사용
2️⃣ @RequestHeader
클라이언트가 요청을 보낼 때, HTTP 헤더에 담긴 값 조회
3️⃣ @ResponseBody
컨트롤러의 메서드 return 값을 HTTP 응답 바디에 직접 포함 HttpMessageConverterrk 가 해줌
💠위의 3가지 예시
@Controller // @RestController = @Controller + @ResponseBody
public class RequestBodyStringController {
@ResponseBody
@PostMapping("/v5/request-body-text")
public String requestBodyTextV5(
@RequestBody String body,
@RequestHeader HttpHeaders headers
) {
// HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
String bodyMessage = body;
return "request header = " + headers + " response body = " + bodyMessage;
}
}
💠 요청,결과
📖 2가지 경우 차이
1 | 폼 데이터를 객체로 매핑 | GET, POST 요청의 쿼리 파라미터 또는 폼 데이터 (x-www-form-urlencoded) | 스프링이 자동으로 객체 필드에 바인딩 | 폼 데이터 전송, URL 파라미터 바인딩 |
2 | 요청 본문(JSON, XML 등)을 객체로 변환 | POST, PUT 요청의 Request Body (JSON, XML) | HttpMessageConverter를 통해 객체 변환 | REST API에서 JSON 데이터를 받을 때 사용 |
💠추가 @ResponseStatus
응답에 상태코드 추가해줌 대신 동적으로는 X
ResponseEntity<Object>(JSON) 사용 시 동적으로 상태코드 추가 가능.
@GetMapping("/v2/response-body")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("data", HttpStatus.OK);
}
@ResponseBody
@GetMapping("/v5/response-body")
public ResponseEntity<Tutor> responseBody() {
Tutor tutor = new Tutor("wonuk", 100);
if (조건) {
return new ResponseEntity<>(tutor, HttpStatus.OK);
} else {
return new ResponseEntity<>(tutor, HttpStatus.BAD_REQUEST);
}
}
- ResponseEntity<>두 번째 파라미터에 Enum을 사용하여 상태 코드를 바꿀 수 있다.
- HTTP Message Converter를 통하여 JSON 형태로 변환되어 반환된다.
- 동적으로 응답 코드를 변경할 수 있다.