SSARTEL-10th / JPTS_bookstudy

"개발자가 반드시 알아야 할 자바 성능 튜닝 이야기" 완전 정복
7 stars 0 forks source link

Spring은 Annotation을 잘 쓰는 게 중요하대.. #14

Open daminzzi opened 9 months ago

daminzzi commented 9 months ago

👍 문제

책의 p.217에서는 이렇게 이야기하고 있다.

그리고, 아무리 스프링 프레임워크가 웹 개발을 편하게 할 수 있도록 되어 있다고 하더라도, 스프링 프레임워크에서 제공하는 각 어노테이션이 어떤 의미인지 정확히 알고 사용해야만 한다.

책에서는 @Transactional, RequestMapping, @PathVariable 등의 어노테이션을 볼 수 있었는데 서블릿도 처음 써보는 내가 이걸 이해할 수 있을리가 없었다. 스프링의 어노테이션을 잘 이용할 수 있도록 스프링에서 주로 사용하는 어노테이션에 대해서 간단히 소개해주면 좋겠다.

✈️ 선정 배경

곧 싸피에서 Spring 진도를 나갈 것 같은데 미리 예습 겸 정리해보면 좋지 않을까해서 주제를 선정해봅니다.. 항상 이런 내용을 검색해보면 정리하는 사람도 모르는 내용을 많이 얻더라구요.. 스프링 응애인 저를 도와주세요!

📺 관련 챕터 및 레퍼런스

ch11. JSP와 서블릿, Spring에서 발생할 수 있는 여러 문제점

🐳 비고

구글에 정리 잘 해둔 블로그가 많더라구요.. 잘부탁드립니다..

kgh2120 commented 9 months ago

들어가며

이번 내용은 Spring과 Spring Boot를 처음 접한 사람들에게 '이런 어노테이션과 이런 기능이 있습니다~'하는 수준으로 소개를 했습니다. 처음에 시작할 때엔 이것저것 욕심이 많았는데, 쓰다 보니 지쳐서 포기했습니다.. 이 전부를 다 외운다고 생각하기보단 그냥 '이런 게 있구나' 하고 기억해 두고, 이후에 검색할 때 키워드가 생각날 수 있다면 좋겠습니다.


어노테이션이란?

Annotation을 사전에 검색하면 '주석'이라는 뜻이 나온다. 하지만 자바에서 일반적으로 사용하는 주석은 //, /* */ 이런 형태인데, 무엇이 주석이라는 말일까. 내가 교육기관에서 들었던 이야기로는 위에서 작성한 형태의 주석(//, /**/)과 달리 사람이 읽는 주석이 아닌, 컴파일러가 읽는 주석이라고 한다.

자바의 어노테이션에 대해서 위키백과에 작성된 이야기는 아래와 같다.

자바 애너테이션(Java Annotation)은 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종이다. 보통 @ 기호를 앞에 붙여서 사용한다. JDK 1.5 버전 이상에서 사용 가능하다. 자바 애너테이션은 클래스 파일에 임베디드되어 컴파일러에 의해 생성된 후 자바 가상머신에 포함되어 작동한다.
위키백과 - 자바 애노테이션

위 인용문에서 이번 포스팅에서 중요한 내용은 메타 데이터의 일종이다. 라는 구문이다. @를 붙이는 것으로 해당 클래스, 메서드, 필드에게 메타 데이터를 추가하고, 어떠한 기술을 통해 원하는 동작을 수행시키는 것이 Spring에서 하는 일이다.

Spring에서 주로 사용하는 어노테이션

Spring에서 사용하는 어노테이션은 굉장히 많다. 그리고 Spring은 개발자가 원하는 어노테이션을 쉽게 만들고 이용할 수 있게 해 준다. 아래 소개할 어노테이션들은 Spring을 이용할 때 굉장히 자주 접하는 어노테이션들이다. 성질이 유사한 어노테이션들은 같은 헤드아래에 두었으며, 각 어노테이션들에 대한 간단한 소개와 사용법을 다룬다.

Bean 등록

아래 어노테이션들은 Spring에서 사용되는 컴포넌트인 Bean과 관련된 어노테이션들이다.

@Bean

이름과 같이 Bean을 등록하는 어노테이션이다. 해당 어노테이션의 docs를 보면 다음과 같이 작성되어 있다.

Indicates that a method produces a bean to be managed by the Spring container.

@Bean은 아래 다른 어노테이션과 다르게 메서드에 붙여서 Bean을 등록한다. Bean은 기본적으론 메서드의 이름을 camel case로 변경한 id로 등록이 되지만 name을 입력하면 그 값으로 등록된다.


@Bean
public MemberService memberService(MemberRepository memberRepository){
    return new MemberServiceImpl(memberRepository());
}

@Bean(name="jdbcMemberRepository")
public MemberRepository memberRepository(){
    return new JDBCMemberRepository();
}

@Bean
public MemberRepository jpaMemberRepository(){
    return new JpaMemberRepository();
}

@Component

컴포넌트 스캔을 통해서 감지되어 자동으로 Bean이 등록될 후보 클래스를 명시한다. 앞서 언급한 것처럼 Method가 아닌 Class단위에 붙이는 어노테이션이다. 아래 등장하는 @Controller 부터 @Configuration 까지는 전부 내부에 @Component 어노테이션을 가지고 있어, 컴포넌트 스캔의 대상이 된다.

@Component
public class MemberServiceImpl implements MemberService{
}

@Controller @RestController

Spring에서 이용될 Controller를 명시하는 어노테이션이다. 아래 등장할 @RequestMapping과 같이 매핑할 URL을 통해 요청을 받는다. @Controller VIEW를 리턴하는데, @ResponseBody 어노테이션을 붙인다면, Body에 다른 값을 담을 수 있다. @RestController@Controller + @ResponseBody이다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
...
}

@ControllerAdvice @RestControllerAdvice

아래 Docs의 내용을 살펴보면, Controller 클래스들에게 선언된 @ExceptionHandler, @InitBinder, @ModelAttribute를 공유한다고 한다.

Specialization of @Component for classes that declare @ExceptionHandler, @InitBinder, or @ModelAttribute methods to be shared across multiple @Controller classes.

@ExceptionHandler@ModelAttribute는 아래에서 다루니 이 부분에서 설명은 생략한다.

@InitBinder 어노테이션은 Controller에 들어오는 요청에 대한 추가 설정을 하는 것이다. 특히 WebDataBinder에 대한 설정을 할 수 있다. WebDataBinder와 @InitBinder에 대한 자세한 정보는 이 블로그에서 확인 가능하다.

주로 사용하는 것은 @ExceptionHandler와 관련된 처리를 하며, 이름에서 알 수 있듯이 @RestControllerAdvice@ControllerAdvice@ResponseBody가 합쳐진 것이다.

@Service

Spring에서 비즈니스 로직을 처리하는 계층에 붙이는 어노테이션이다.

@Repository

Spring에서 DB관련 로직을 처리하는 계층에 붙이는 어노테이션이다.

@Configuration

@Bean 을 붙인 메서드들을 Bean으로 등록할 수 있는 어노테이션이다.

아래 내용은 Spring Docs에 작성된 내용으로, @Configuration의 주된 목적은 Bean을 정의하는 것이라고 한다.

Annotating a class with @Configuration indicates that its primary purpose is as a source of bean definitions.

요청/응답

아래 설명할 어노테이션들은 요청, 응답 시 사용되는 어노테이션들이다. 대체로 @Controller, @RestController 내부에서 사용되는 경우가 많다.

URL Mapping

@RequestMapping

요청에 대한 URL을 매핑하는 어노테이션이다. 다음과 같이 URL을 매핑할 수 있다. 아무것도 입력하지 않으면 모든 메서드에 대한 요청을 받는다. 두 번째 메서드(hi())처럼 HTTP 메서드를 지정하면, 해당 메서드에 대한 응답만을 받을 수 있다.


@RequestMapping("/hello")
public String hello(){
    return "hello?";
}

@RequestMapping(value = "/hi", method = RequestMethod.GET)
public String hi(){
    return "hi?";
}

@RequestMapping은 클래스 레벨에도 적용이 가능한 메서드이기 때문에, prefix가 있는 url의 경우 클래스 부분에 선언하면 쉽게 처리할 수 있다.
아래 코드에서 member()의 실제 url은 /member/get이 된다.

@RequestMapping("/member")
@RestController
public class MemberController {
    @GetMapping("/get") // 실제 url은 /member/get
    public String member(){
        return "나는 멤버";
    }
}

아래 어노테이션들은 위에서 보여준 @RequestMapping의 HTTP 메서드 명시 부분을 미리 선언한 어노테이션들이다. @RequestMapping를 사용하는 것보다 아래 어노테이션들을 사용하는 것을 더 추천한다.

@GetMapping @PostMapping @PutMapping @PatchMapping @DeleteMapping

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
}

요청

아래 있는 어노테이션은 컨트롤러에서 요청을 받을 때 사용된다.

@RequestParam

QueryString과 x-www-form-urlencoded에 관한 요청을 받는다.
아래 코드는 /hello?name=...&age=... 에 대한 요청을 받는 코드이다. 본래 Servlet에서는 Parameter가 String으로 넘어오지만, Spring에서 기본으로 설정해 주는 부분으로 인해 String, int와 같은 변수는 별도의 설정 없이 타입이 변환된다. 받겠다고 선언한 파라미터가 없을 경우 예외가 발생하며, required=false를 설정해 주면 예외가 발생하지 않는다.

@GetMapping("/hello") // queryString
public void hello(@RequestParam("name") String name, @RequestParam("age") int age){
 ...
}

@PostMapping("/login") // x-www-form-urlencoded
public void login(@RequestParam String id, @RequestParam String password){
 ...
}

@RequestBody

Body에 대한 요청을 처리한다. 일반적으로 Spring에서는 application/json에 대한 요청을 객체화시켜 주는 작업을 한다. 아래와 같이 Map 혹은 Java 객체로 받을 변환이 된다. 찾아보니 이는 Spring이 Jackson 라이브러리를 이용하기 때문에 json 요청을 객체로 변환시킬 수 있는 것이고, 다른 라이브러리를 이용하면 JSON이 아닌 다른 타입으로 받을 수 있다고 한다. 더 자세한 내용이 궁금하다면 이 블로그를 참고하길 바란다.

    @PostMapping(value = "/post")
    public Map<String, Object> post(@RequestBody Map<String, Object> dto) {
        ...
    }
    @PostMapping(value = "/post")
    public Map<String, Object> post(@RequestBody RequestDto dto) {
        ...
    }

@RequestHeader

요청에서 들어온 Header의 값을 전달받는다. 아래와 같은 형태로 이용한다.

    @PostMapping(value = "/header")
    public Map<String, Object> post(@RequestHeader("EX-HEADER") String header){
        ...
    }

@PathVariable

URL에 변수를 이용할 수 있게 해 준다. 아래와 같이 사용할 수 있다. PathVariable은 검색을 하면서도 굉장히 자주 볼 수 있는 부분이기 때문에, 이해하기 가장 쉬울 것 같다.

    @GetMapping("/{memberId}")
    public String pv(@PathVariable("memberId") String memberId) {
     ...
    }

@RequestPart

multipart/form-data의 데이터를 받는 데 특화된 어노테이션이다. 여러 개의 데이터가 서로 다른 타입으로 들어올 때 이용가능하다. 아래 예시 코드처럼 회원가입을 한다고 할 때 입력한 정보와 프로필 사진을 동시에 보내고 싶을 때 이용가능하다.

    @PostMapping("/multipart")
    public void signUp(@RequestPart("userInfo") UserInfo userInfo, @RequestPart("profileImg")
            MultipartFile multipartFile) {

    }

@ModelAttribute

Spring MVC 이용한다면 자주 사용할 어노테이션으로 요청으로부터 받은 값을 객체로 변환하여 주고, 그 객체를 Model에 담아준다. (Model에 담을 경우 템플릿 엔진에서 이용가능하다.)

    @PostMapping("/item")
    public String registerItem(@ModelAttribute Item item){
        ...
    }

응답 시 사용

@ResponseBody

응답을 할 때, Body에 데이터가 담긴다는 것을 명시한다. 사용하지 않을 경우, Spring에선 html 혹은 JSP, Thymeleaf와 같은 템플릿 엔진의 파일의 주소로 인식하여, 그 파일을 반환하려 한다.

@ResponseStatus

응답을 할 때, 응답 코드를 설정한다. 아래와 같이 사용할 수 있다.

    @PostMapping(value = "/post")
    @ResponseStatus(HttpStatus.CREATED)
    public Map<String, Object> post(@RequestBody Map<String, Object> dto) {
        ...
    }

ETC

@Autowired

의존성을 주입해 주는 어노테이션이다. Spring에서 의존성을 주입하는 방법은 3가지가 있는데, 필드 주입, setter주입, 생성자 주입에서 사용된다.

필드 주입

필드에 직접 @Autowired 어노테이션을 붙인다.

@RestController
public class MemberController {

    @Autowired
    private MemberService memberService;
         ...   
}

setter 주입

setter 메서드에 @Autowired 어노테이션을 붙인다. 런타임에서 의존성을 변경할 수 있다는 장점이 있다고 하는데, 그럴 일은 거의 없다고 한다.

@RestController
public class MemberController {

    private MemberService memberService;

    @Autowired
    public void setMemberService(MemberService memberService){
        this.memberService = memberService;
    }
    ...
}

생성자 주입

가장 많이 사용하는 방법인 생성자 주입이다. 그리고 이 방법을 가장 권장한다. 생성자 위에 @Autowired를 붙여준다. 생성자가 하나밖에 없을 경우, 그 생성자 기준으로 의존성 주입이 일어난다고 한다.

@RestController
public class MemberController {

    private MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }
}

@ExceptionHandler

명시한 예외가 들어올 경우, 그에 대한 처리를 해주는 어노테이션이다. 아래와 같은 형태로 주로 사용한다. 어떤 예외를 처리할 것인지에 대해서 명시하고, 그에 해당하는 작업을 처리한다.


   @ExceptionHandler(JsonParseException.class)
    public ResponseEntity<ErrorResponse> handleJsonParseException(HttpServletRequest request) {
        ...
    }

@Value

설정 파일(.properties, .yml)에 입력된 값을 변수에 담을 수 있다.

    @Value("${key.kakao}")
    private String key;

참고 자료

자바 어노테이션 사용법 및 예제
RequestPart vs RequestParam
Spring's RequestBody and ResponseBody Annotations
How to force Spring boot's @RequestBody to accept XML
XML/JSON 변환 처리(@RequestBody, @ResponseBody, HttpMessageConverter)
[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2)
스프링(Spring)에서 자주 사용하는 Annotation 개념 및 예제 정리

KIMSEI1124 commented 9 months ago

@ExceptionHandler({JsonParseException.class, APIException.class})