스프링은 다양한 어노테이션을 제공합니다. @Controller, @Repository와 같은 스프링 빈의 핵심 어노테이션도 있지만, 사용성을 편리하게 해주는 어노테이션도 있습니다.
Lombok은 도메인 설계를 비롯한 다양한 핵심 기능을 어노테이션으로 제공하고 있습니다. 그 중 오늘 정리할 어노테이션은 @AllArgsConstructor입니다.
@AllArgsConstructor를 정리하는 이유는 자동 생성자 어노테이션의 오용을 막기 위함입니다.
@NoArgsConstructor
@AllArgsConstructor
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String name;
}
AllArgsConstructor는 클래스의 모든 필드 변수로 생성자를 만들어주는 어노테이션입니다.
Member 클래스에 @AllArgsConstructor 어노테이션을 적용하면, id, name 필드로 구성된 생성자를 생성해줍니다.
// MemberRestController
@PostMapping("/api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(
@PathVariable("id") Long id,
@RequestBody UpdateMemberRequest updateMemberRequest) {
memberService.update(id, updateMemberRequest.getName());
Member findMember = memberService.findOne(id);
return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}
// MemberRestController의 내부 클래스
@Getter
@AllArgsConstructor
static class UpdateMemberRequest {
private String name;
}
위 코드는 Json으로 구성된 회원의 이름을 입력받아, 회원의 이름을 수정하는 컨트롤러입니다.
UpdateMemberRequest는 Json을 파싱하여 Member 도메인에 적용하기 위해 사용하는 Dto입니다. 위 코드로 실행할 경우, 다음의 에러가 발생합니다.
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of ~ ... 생략 ... jackson.databind.exc.MismatchedInputException:
이 에러는, jackson 라이브러리가 json입력을 받아서 파싱하는 과정에서 일어난 에러입니다.
스프링 및 jackson을 비롯한 기능은 Reflection을 제공합니다. Reflection은 컴파일 시점에 객체 타입을 결정하는 Java를 보완하는 기술로, 접근 제어자와 상관 없이 클래스 객체를 동적으로 생성하는(런타임 시점) 기술입니다.
다양한 글을 읽고, 제가 이해한 부분을 바탕으로 에러 상황을 정리해보겠습니다.
@AllArgsConstructor는 모든 필드의 값으로 생성자를 만들어주는 간단한 어노테이션이지만, 스프링이 동작하는 과정에서 Reflection 기능이 적용됩니다. 즉, 컴파일 시점에 객체를 생성하는 것이 아니라, 런타임 시점에 객체를 생성하는데, 기본 생성자가 있어야 해당 객체를 선언하고 값을 주입할 수 있기 때문입니다.
따라서, @AllArgsConstructor만 어노테이션으로 존재한다면, 생성자가 없이 필드 주입을 필요로 하는 생성자가 적용되므로, Reflection이 해당 클래스를 처리하지 못해 발생한 에러입니다.
@NoArgsConstructor를 추가해주거나, @AllArgsConstructor를 빼면 정상동작하는 것을 확인할 수 있었습니다.
어노테이션은 코딩에 편리함을 제공하지만, 오남용할 경우에 큰 에러를 발생할 수 있다는 점을 상기시켜주는 시간이었습니다. 평소에, 다양한 Lombok 어노테이션을 의미와 역할을 모르고 무작정 사용했었는데, @AllArgsConstructor를 비롯한 @Setter 등 큰 에러를 발생시킬 수 있는 어노테이션을 자제하는 코딩 습관을 가질 필요성이 있습니다.
잘못된 부분 및 부족한 부분에 대해서 리뷰해주시면 정말 감사하겠습니다.!
항상 배우는 자세로 개발에 임하겠습니다.
참조 : https://da-nyee.github.io/posts/woowacourse-why-the-default-constructor-is-needed/