-

29일차)

생성자 주입

불변(immutable)과 실수 방지를 위함

-> 순수 Java 코드로 사용할 때 (ex. 테스트 코드) 생성자의 필드를 필수로 입력하도록 만들어준다. (NPE 방지)

-> 컴파일 시점에 오류를 발생시킨다. 즉, 실행 전에 오류를 알 수 있다.

// 생성자 주입 방식
@Component
public class MyApp {
		// 필드에 final 키워드 필수! 무조건 값이 있도록 만들어준다.(필수)
    private final MyService myService;

		// 생성자를 통해 의존성 주입, 생략 가능
    @Autowired
    public MyApp(MyService myService) {
        this.myService = myService;
    }

    public void run() {
        myService.doSomething();
    }
    
}

public class Main {
    public static void main(String[] args) {
		    // MyService가 매개변수로 꼭 들어가야 한다.
        MyApp myApp = new MyApp();
        myApp.run();
    }
}

 

@RequiredArgsConstructor 

생성자 주입 시 반복되는 코드를 편안하게 작성하기 위해 Lombok에서 제공하는 Annotation

final 필드를 모아서 생성자를 자동으로 만들어줌

@Component
@RequiredArgsConstructor
public class MyApp {
		// 필드에 final 키워드 필수! 무조건 값이 있도록 만들어준다.(필수)
    private final MyService myService;
    
    // Annotation Processor가 만들어 주는 코드
    // public MyApp(MyService myService) {
    //     this.myService = myService;
    // }

    public void run() {
        myService.doSomething();
    }
    
}

 

@BindingResult

case1. 파라미터에 BindingResult가 없으며, 오류가 발생한 경우

- 404 Bad Request 발생, Controller 호출되지 않음

// View 반환
@Controller
public class BingdingResultController {
	
    @PostMapping("/v1/member")
    public String createMemberV1(@ModelAttribute MemberCreateRequestDto request, Model model) {
        // Model에 저장
        System.out.println("/V1/member API가 호출되었습니다.");
        model.addAttribute("point", request.getPoint());
        model.addAttribute("name", request.getName());
        model.addAttribute("age", request.getAge());

        // Thymeleaf Template Engine View Name
        return "complete";
    }

}

 

 

case2. 파라미터에 BindingResult가 있으며, 오류가 발생한 경우

- BindingResult에 오류가 보관되고 Controller는 정상적으로 호출됨

- @ModelAttribute는 파라미터를 필드 하나하나에 바인딩한다. 파라미터에 Binding Result가 함께 있는 경우 만약 그 중 하나의 필드에 오류가 발생하면 해당 필드를 제외하고 나머지 필드들만 바인딩 된 후 Controller가 호출된다.

@Controller
public class BindingResultController {

    @PostMapping("/v2/member")
    public String createMemberV2(
            // 1. @ModelAttribute 뒤에 2. BindingResult가 위치한다.
            @ModelAttribute MemberCreateRequestDto request,
            BindingResult bindingResult,
            Model model
    ) {

        System.out.println("/V2/member API가 호출되었습니다.");

        // BindingResult의 에러 출력
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        System.out.println("allErrors = " + allErrors);

        // Model에 저장
        model.addAttribute("point", request.getPoint());
        model.addAttribute("name", request.getName());
        model.addAttribute("age", request.getAge());

        return "complete";
    }
    
}

 

- 결과 : 입력이 잘못된 'point'는 출력x

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>
    <h1>Member 생성이 완료되었습니다!</h1>
    <ul>
        <li><span></span></li>
        <li><span>이름</span></li>
        <li><span>100</span></li>
    </ul>
</body>

</html>

 

- 콘솔 결과

V2/member API가 호출되었습니다.
allErrors = [Field error in object 'memberCreateRequestDto' on field 'point': rejected value [ㅎㅇ]; codes [typeMismatch.memberCreateRequestDto.point,typeMismatch.point,typeMismatch.java.lang.Long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [memberCreateRequestDto.point,point]; arguments []; default message [point]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Long' for property 'point'; For input string: "ㅎㅇ"]]