목차
이번 포스팅은 3주차 배운 내용 보다는 스터디 주제에 대해 공부한 내용을 정리할 생각이다.
스터디 주제는 : DI, Bean, Configuration, Controller, Rest Controller, ControllerAdvice, InterCeptor, HTTP method, status code
DI (Dependency Injection)
의존대상 B가 변하면, 그것이 A에 영향을 미친다. (이일민, 토비의 스프링 3.1)
public class A {
private B b = new B();
}
A라는 클래스가 있고, 이 클래스는 B라는 클래스를 필드로 가질때 B에 final 필드가 추가되는 변경이 일어난다면
new B()부분에서 컴파일 에러가난다. B 내부의 변경이 일어났는데, A에도 영향을 미치게 된것이다.
이런 경우를 A가 B에 의존한다. 라고한다. 그렇다면 의존성 주입이란, 의존성을 외부에서 주입해준다는 의미가 될것이다. 예를 들어 다음과 같은 코드이다.
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
B가 변경되면 A의 내용도 변경되지만, 의존 대상을 직접 생성하는 것이 아닌 외부로 부터 주입을 받는다.
의존성을 주입하는 방법에는 세가지가 있다.
생성자 주입(Constructor Injection)
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
Setter 주입(Setter Injection)
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
인터페이스 주입(Interface Injection)
public interface BInjection {
void inject(B b);
}
public A implements BInjection {
private B b;
@Override
public void inject(B b) {
this.b = b;
}
}
그렇다면 왜 DI를 써야하는가?
- 의존성이 줄어든다 (변경에 덜 취약해진다.)
- 모의 객체를 주입할 수 있기 때문에 단위 테스트가 쉬워진다.
- 가독성이 높아진다.
- 재사용성이 높아진다.
빈(Bean)
스프링(Spring) 컨테이너가 관리하는 자바 객체를 빈(Bean)이라 한다.
스프링의 특징에는 제어의 역전(IoC)가 있다.
지금까지 사용자가 new연산을 통해 객체를 생성하고 메소드를 호출했지만 제어의 역전이 적용 된 경우에는 객체의 생성과 사용자의 제어권을 스프링에게 넘긴다. 사용자는 직접 new를 이용해 생성한 객체를 사용하지 않고, 스프링에 의하여 관리당하는 자바 객체를 사용한다. 이 객체를 '빈(bean)' 이라 한다.
스프링 빈을 스프링 컨테이너에 등록하는 방법
- @Component 있으면 스프링 빈으로 자동 등록된다.
Component를 포함하는 @Controller, @Service, @Repository 도 스프링 빈으로 자동 등록된다.
- @Configuration과 @Bean 애노테이션을 이용해 스프링 빈을 등록한다.
Configuration
Spring에서 빈을 수동으로 등록하기 위해선, class위에 @Configuration을 추가하고, @Bean을 사용해 수동으로 등록할 ㅅ 수 있다.
@Configuration 내부를 보면 @Component가 추가되어 있다. 그래서 해당 Class또한 빈으로 등록된다.
이후 해당 클래스의 본문을 파싱해서 @Bean이 붙어있는 메서드를 찾아서 빈을 생성한다.
@Configuaration의 역할
- 빈을 등록할때 싱글톤이 되도록 보장해준다.
- 스프링 컨테이너에서 빈을 관리할수 있게된다.
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
public class MyBeanConsumer {
public MyBeanConsumer(MyBean myBean) {
System.out.println(MyBeanConsumer created);
}
}
위와 같은 클래스가 있고
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
retrun new MyBean();
}
@Bean
public MyBeanConsumer myBeanConsumer() {
return new MyBeanConsumer(myBean());
}
}
사용할 설정파일이 위와 같을 때
- 빈들은 싱글톤으로 등록되기 때문에 MyBean instance created는 단 1번만 출력된다.
- 하지만 @Configuration을 제거하면 myBean() 메서드가 호출될 때마다 new로 새로운 MyBean을 생성하기 때문에 MyBean instance created이 2번 출력된다.
- @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다. 덕분에 싱글톤이 보장되는 것이다.
Controller
1. @Controller 어노테이션의 역할
- @Controller 어노테이션은 말 그대로, 해당 클래스가 웹 애플리케이션의 컨트롤러임을 나타낸다.
- 스프링 컨테이너는 @Controller 어노테이션이 지정된 클래스를 빈으로 등록하고, 요청을 해당 컨트롤러에 매핑하여 처 리한다.
- @Controller 어노테이션은 클라이언트의 요청을 처리하는 비즈니스 로직과 뷰를 결합하여 전체적인 동작을 제어한다.
2. @Controller 어노테이션의 사용 방법
- @Controller 어노테이션이 지정된 클래스는 일반적으로 HTTP 요청을 처리하기 위한 메소드를 포함한다.
- 각 메소드는 @RequestMapping 어노테이션과 함께 사용하여 특정 URL 패턴에 대한 처리를 정의한다.
- @RequestMapping 어노테이션을 사용하여 요청 URL과 해당 메소드를 매핑시키고, 메소드는 요청을 처리하고 필요한 로직을 수행한 후에 응답을 생성한다.
- 컨트롤러 메소드에서는 ModelAndView, Model, ResponseEntity 등을 반환하여 응답 데이터와 뷰 정보를 제공한다.
3. @Controller 어노테이션의 추가 기능
- @PathVariable 어노테이션을 사용하여 경로 변수를 추출하고 활용할 수 있다.
- @RequestParam 어노테이션을 사용하여 요청 파라미터를 추출하고 활용할 수 있다.
- @ModelAttribute 어노테이션을 사용하여 요청 데이터를 객체에 바인딩하고, 자동으로 모델에 추가할 수 있다
.
4. @Controller 어노테이션의 예시
@Controller
@RequestMapping("/products")
public class ProductController {
@Autowired
private productService productService;
@RequestMapping("/{id}")
public ModelAndView getProduct(@PathVariable("id")int id) {
Product product = productService.getProductById(id);
ModelAndView modelAndView = new ModelAndView("productView");
modelAndView.addObject("products", products);
return modelAndView;
}
}
위의 예시에서는 "/products"로 시작하는 URL에 대한 요청을 처리하기 위한 컨트롤러이다.
@RequestMapping 을 사용하여 각 요청 URL과 해당 메소드를 매핑하고, ProductService를 이용해 비즈니스 로직을 수행한다.
RestController
@RestController는 @Controller에 @ResponseBody가 추가된 것이다.
@RestController의 주용도는 JSON 형태로 객체를 반환하는 것이고, 최근에는 데이터를 응답으로 제공하는 REST API를 개발할 때 주로 사용하며 객체를 ResponseEntity로 감싸서 반환한다.
ControllerAdvice
콘솔 애플리케이션을 구현할 때, 예외를 다루기 위해 try/catch문을 사용했지만, 웹 애플리케이션 에서는 예외 처리 방법이 다르다.
스프링부트의 기본적인 예외처리
@RestController
public class SimpleController {
@GetMapping(path = "/errorExample")
public String invokeError() {
throw new IllegalArgumentException();
}
}
예외를 던지는 메서드를 호출하는 URL/errorExample에 요청을 보내면 기본 에러 페이지가 표시된다.
하지만 이 방법은 url만 알면 누구나 마음대로 error 페이지에 접근할 수 있다는 단점이있다.
이 단점을 @ExceptionHandler를 사용해 해결할 수 있다.
@ExceptionHandler
이 어노테이션을 사용하면 value로 원하는 예외를 지정하고 이를 핸들링할 수 있다. 또한 예외에 대한 세부적인 정보 또한 응답으로 전달해 줄 수 있다.
@RestController
public class SimpleController {
// ...
@ExceptionHandler(value = IllegalArgumentException.class)
public ResponseEntity<String> invokeError(IllegalArgumentException e) {
...
return new ResponseEntity<>("error Message", HttpStatus.BAD_REQUEST);
}
}
위 처럼 컨트롤러 별로 반환하고자 하는 body와 메시지를 예외별로 적절하게 선택하여 세밀한 정보를 제공한다.
하지만 @ExceptionHandler는 코드를 작성한 컨트롤러에서만 발생하는 예외만 처리되는데, 같은 예외에 대해 여러 컨트롤러에서 같은 처리를 하고 싶다면 컨트롤러마다 같은 메서드를 작성해주어야한다.
--> 코드의 중복이 발생한다.
@ControllerAdvice
@ControllerAdvice는 @Component어노테이션의 특수 케이스로 스프링부트 애플리케이션에서 전역적으로 예외를 핸들링할 수 있게 해주는 어노테이션이다.
이를 통해 코드의 중복을 해결할 수 있다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
...
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
...
}
위는 @ControllerAdvice와 @RestControllerAdvice 구현의 일부인데
@Component 어노테이션을 붙이면 Component Scan과정을 거쳐 Bean으로 등록되고 이 어노테이션을 사용하는 ControllerAdvice 또한 Bean으로 관리된다.
또한 @RestControllerAdvice는 @ResponseBody 어노테이션이 붙어있으므로 응답을 Json으로 처리한다.
*주의점 : 여러 ControllerAdvice가 있을때 @Order어노테이션으로 순서를 지정하지 않는다면 스프링은 임의의 순서로 호출한다. 즉, 사용자가 예상하지 못한 예외처리가 발생한다.
Interceptor
컨트롤러(Controller)의 '핸들러(Handler)'를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할수 있는 일종의 필터
interceptor 란 단어는 '낚아채다'라는 의미이다. 사용자 요청에 의해 서버에 들어온 Request 객체를 컨트롤러의 핸들러로 도달하기전에 낚아채서 개발자가 원하는 추가적인 작업을 한 후 핸들러로 보낼 수 있도록 해주는것이 인터셉트이다.
Interceptor를 사용하는 이유
개발자는 특정 Controller의 핸들러가 실행되기 전이나 후에 추가적인 작업을 원할때 인터셉터를 사용한다.(ex : 로그인체크, 권한 체크)예를 들어, 개발자가 관리자 계정만이 실행할 수 있는 Controller 핸들러를 작성한다고 가정할때,개발자는 오직 관리자 계정만 실행할 수 있도록 하기 위해 핸들러에 접근하는 사용자가 관리자인지 확인하는 세션 체크 코드를 각 핸들러에 작성해주어야한다.작성해주어야 할 핸들러 수가 적다면 문제가 되지않지만, 적용해야할 핸들러가 수천개가 된다면 메모리 낭비, 서버의 부하가 늘어난다.
Interceptor의 구현수단
org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현하거나 org.springframework.web.servlet.handler.HandlerInterceptorAdapter 추상클래스를 오버라이딩함으로써 자신만의 인터셉터를 만들 수 있다.
Interceptor의 메서드
1) preHandle()
- 컨트롤러가 호출되기 전에 실행됨
- 컨트롤러가 실행 이전에 처리해야 할 작업이 있는경우 혹은 요청정보를 가공하거나 추가하는경우 사용
- 실행되어야 할 '핸들러'에 대한 정보를 인자값으로 받기때문에 '서블릿 필터'에 비해 세밀하게 로직을 구성할수 있음
- 리턴값이 boolean이다. 리턴이 true 일경우 preHandle() 실행후 핸들러에 접근한다. false일경우 작업을 중단하기 때문에 컨트롤러와 남은 인터셉터가 실행되지않는다.
2) postHandle()
- 핸들러가 실행은 완료 되었지만 아직 View가 생성되기 이전에 호출된다.
- ModelAndView 타입의 정보가 인자값으로 받는다. 따라서 Controller에서 View 정보를 전달하기 위해 작업한 Model 객체의 정보를 참조하거나 조작할수 있다.
- preHandle() 에서 리턴값이 fasle인경우 실행되지않음.
- 적용중인 인터셉터가 여러개 인경우, preHandle()는 역순으로 호출된다.
- 비동기적 요청처리 시에는 처리되지않음.
3) afterCompletion()
- 모든 View에서 최종 결과를 생성하는 일을 포함한 모든 작업이 완료된 후에 실행된다.
- 요청 처리중에 사용한 리소스를 반환해주기 적당한 메서드 이다.
- preHandle() 에서 리턴값이 false인경우 실행되지 않는다.
- 적용중인 인터셉터가 여러개인경우 preHandle()는 역순으로 호출된다.
- 비동기적 요청 처리시에 호출되지않음.
인터셉터 동작 위치 및 순서
1) 사용자는 서버에 자신이 원하는 작업을 요청하기 위해 url을 통해 Request 객체를 보낸다.
2) DispatcherServlet은 해당 Request 객체를 받아서 분석한뒤 '핸들러 매핑(HandlerMapping)' 에게
사용자의 요청을 처리할 핸들러를 찾도록 요청 한다.
3) 그결과로 핸들러 실행체인(HandlerExecutionChain)이 동작하게 되는데, 이 핸들러 실행체인은 하나이상의 핸들러 인터셉터를 거쳐서 컨트롤러가 실행될수 있도록 구성되어 있다.
(핸들러 인터셉터를 등록하지 않았다면, 곧바로 컨트롤러가 실행된다. 반대로 하나이상의 인터셉터가 지정되어 있다면 지정된 순서에 따라서 인터셉터를 거쳐서 컨트롤러를 실행한다)
HTTP Method
HTTP 메서드란 클라이언트와 서버 사이에 이루어지는 요청(Request)과 응답(Response) 데이터를 전송하는 방식이다.
HTTP 메서드는 총 9가지 있는데 주로 5가지가 많이 쓰인다.
- GET : 리소스 조회
- POST : 요청 데이터 처리, 주로 등록에 사용
- PUT : 리소스를 대체(덮어쓰기), 해당 리소스가 없으면 생성
- PATCH : 리소스 부분 변경 (PUT이 전체 변경, PATCH는 일부 변경)
- DELETE : 리소스 삭제
- HEAD : GET과 동일하지만 메시지 부분(body 부분)을 제외하고, 상태 줄과 헤더만 반환
- OPTIONS : 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명 (주로 CORS에서 사용)
- CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정
- TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행
Status Code
HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려준다.
응답은 5개의 그룹으로 나누어진다. 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 서버에러
1XX: Informational(정보 제공)
- 임시 응답으로 현재 클라이언트의 요청까지는 처리되었으니 계속 진행하라는 의미
2XX: success(성공)
- 클라이언트의 요청이 서버에서 성공적으로 처리되었다는 의미
3XX: Redirection(리다이렉션)
- 완전한 처리를 위해서 추가동작이 필요한 경우. 주로 서버의 주소 또는 요청한 URI의 웹문서가 이동되었으니 그 주소로 다시 시도하라는 의미
4XX: Client Error(클라이언트 에러)
- 없는 페이지를 요청하는 등 클라이언트의 요청 메시지 내용이 잘못된 경우
5XX: Server Error(서버에러)
- 서버 사정으로 메시지 처리에 문제가 발생한 경우. 서버의 부하, DB 처리 과정 오류, 서버에서 예외발생하는 경우를 의미
1. 의존성을 주입하는 방법에는 무엇이 있는가? 각 방법의 장단점은 무엇인가? 어떤 기준으로 나눠서 사용하는가?
생성자 주입, setter 주입, 인터페이스 주입
- 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장된다. 그렇기 때문에 주입받은 객체가 변하지 않고 다시 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있다. 또한 생성자가 1개만 있을 경우에 @Autowired를 생략해도 주입이 가능하도록 편의성을 제공함
- 수정자 주입은 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.
@Autowired로 주입할 대상이 없는 경우에는 오류가 발생한다.
2. DI란 무엇인가? DI를 사용하면 무엇이 좋은가?
DI란 의존관계를 외부에서 결정하고 주입하는 것
1) 의존성이 줄어든다.
2) 재사용성이 높은 코드가 된다.
3) 테스트하기 좋은 코드가 된다.
4) 가독성이 높아진다.
3. 빈의 스코프는 무엇인가?
일반적으로 빈이라고 생각하는 것은, 스프링 컨테이너가 시작될 때 함께 생성되고, 스프링 컨테이너가 종료될 때까지 유지되는 것. 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다.
싱클톤 스코프라 함은 스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위의 스코프이다.
4. 모든 객체를 스프링 빈으로 등록해도 괜찮나? 안된다면 특정 객체만 빈으로 등록해야 하는 이유는 무엇인가?
복잡한 의존성을 스프링이 전부 처리해 줄 수 있어서 괜찮을 수 있지만, 사용자가 생성자를 사용할 수 없다. 만약 Car라는 클래스에 이름을 정의하는 name필드가 추가됐을 때 의존성이 있는 객체가 아닌 사용자의 입력 같은 예측할 수 없는 값은 스프링이 주입해 줄 수 없다.
따라서 객체의 생성이 끝나고 또 한번 객체의 속성을 초기화해야하며 이럴 경우 final 키워드를 사용할 수 없고 컴파일 단계에서 실수를 찾을 수 없기때문에 특정 객체만 빈으로 등록해야 할지 적절한 판단이 필요하다.
5. 빈으로 등록하는 것과 static 메서드를 사용할 때를 구분하는 기준은 무엇인가?
파라미터가 동일할 경우 항상 동일한 결과를 리턴하는지 여부이다.
6. Configuration을 관리하는 방법은 어떤 것이 있는가? 각 방법의 장단점은 무엇인가?
[@Bean, @Configuration]
- 수동으로 컨테이너에 빈을 등록하는 방법
- 개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용
- 유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체들을 등록할 때 사용
[@Component[
- 자동으로 스프링 컨테이너에 빈을 등록하는 방법
- 스프링의 컴포넌트 스캔 기능이 @Component 어노테이션이 있는 클래스를 자동으로 찾아서 빈으로 등록
- 대부분의 경우 @Component를 이용한 자동 등록 방식을 사용하는 것이 좋음
7. @Controller와 @RestController의 역할과 차이점은?
@Controller의 역할은 Model 객체를 만들어 데이터를 담고 View를 찾는 것
@RestController는 단순히 객체만을 반환하고 객체 데이터는 JSON 또는 XML 형식으로 HTTP 응답에 담아서 전송
@RestController를 표시하면 모든 메서드가 뷰 대신 객체로 작성됨
8. ControllerAdvice를 사용할 수 있는 사례는 무엇이 있는가?
9. Interceptor와 Filter의 역할과 차이점은?
Filter : 디스패처 서블릿에 요청이 전달되기 전/후에 url패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능 제공
Interceptor : 디스패처 서블릿이 컨트롤러의 핸들러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능 제공
10. HTTP 메서드 종류에는 무엇이 있는가? 각각의 목적은 무엇인가?
- GET : 리소스 조회
- POST : 요청 데이터 처리, 주로 등록에 사용
- PUT : 리소스를 대체(덮어쓰기), 해당 리소스가 없으면 생성
- PATCH : 리소스 부분 변경 (PUT이 전체 변경, PATCH는 일부 변경)
- DELETE : 리소스 삭제
11. HTTP 상태 코드 중 200, 400, 500번 대의 차이는 무엇인가?
2XX: success(성공)
- 클라이언트의 요청이 서버에서 성공적으로 처리되었다는 의미
4XX: Client Error(클라이언트 에러)
- 없는 페이지를 요청하는 등 클라이언트의 요청 메시지 내용이 잘못된 경우
5XX: Server Error(서버에러)
- 서버 사정으로 메시지 처리에 문제가 발생한 경우. 서버의 부하, DB 처리 과정 오류, 서버에서 예외발생하는 경우를 의미
'🚣활동 > 멋쟁이사자처럼' 카테고리의 다른 글
[멋사 12기 중앙해커톤 참여] (0) | 2024.08.09 |
---|---|
[멋쟁이사자처럼] 4주차 정기세션 (0) | 2024.03.31 |
[멋쟁이사자처럼] 2주차 정기세션 (0) | 2024.03.14 |
[멋쟁이사자처럼] 1주차 정기세션 - 2 (0) | 2024.03.13 |
[멋쟁이사자처럼] 1주차 정기세션 - 1 (0) | 2024.03.13 |