안녕하세요!
지난 시간에 @Autowired라는 어노테이션을 통해 의존관계를 주입해 봤는데요! 하다 보면서 드는 생각은 어차피 의존관계 주입할 필드는 다 final을 쓸 거고, 다 생성자 만들어서 @Autowired 해줄 건데 엄청 반복적이면서 코드 길이만 늘리는 작업인 것 같아요.
저만 이런 생각을 한 것은 아닌가 봐요! 다른 엄청 똑똑한 개발자들이 이를 더욱더 간단하게 만들어줄 엄청난 기능을 만들어냈는데요. 바로 롬복이라는 것입니다!
(Lombok Project (고추..인가?))
롬복을 사용하면서 어떻게 더 간단하게 작업을 할 수 있게 되는지 알아봅시다!
롬복 초기설정
롬복은 기본 기능이 아닌 외부 라이브러리이기 때문에 설정을 해 줄 게 있습니다. 물론 우리는 일정 부분 이미 되어있지만 어떤 설정들이 롬복 설정인지확인해 봅시다.
Gradle 설정
build.gradle로 들어가 줍시다!
설정을 보게 되면 아래 두 부분이 롬복과 관련된 설정입니다.
...
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
...
dependencies {
...
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 이 부분은 추가로 작성
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
// 여기까지
...
}
...
우리가 프로젝트를 생성하면서 롬복 기능을 추가했던 것을 기억하실 분이 아마 있을 것입니다. 그래서 추가가 되어 있고, test에서도 롬복을 사용하기 위해 주석 사이에 있는 저 두줄도 추가로 입력해 주세요.
그리고 아래 사진과 같이 우측 위에 있는 코끼리를 클릭해 주세요!
Plugin 설정
그리고 플러그인을 하나 설치해줘야 합니다. Ctrl+Alt+S를 눌러 설정창에 들어오신 후 왼쪽 메뉴들 중 Plugins에 들어옵시다.
Marketplace에서 Lombok을 검색하신 후 Install 해주시면 됩니다.
IntelliJ 설정
마지막으로 하나 더 설정해 줄게 남았습니다. 현재 이 Settings창의 왼쪽에 아래 사진과 같은 경로로 들어와 줍니다.
그리고 오른쪽 부분 맨 위 Enable annotation processing을 체크해 주시고 OK!
그 후 IntelliJ를 한번 껐다가 켜봅시다.
@RequiredArgsConstructor 적용
이제 한번 써봐야겠죠? 저희가 의존성 주입을 했던 MemberServiceImpl, OrderServiceImpl에서 의존성 주입을 더 간단하게 만들어봅시다.
package com.naver.shopping.order;
import com.naver.shopping.member.Member;
import com.naver.shopping.member.MemberRepository;
import com.naver.shopping.discount.DiscountPolicy;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor // <-- 추가됨
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// 생성자 사라짐
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
(MemberServiceImpl은 직접 한번 적용해 보세요!)
@RequiredArgsConstructor 어노테이션 하나를 썼다고 이렇게나 간결해지다니!! rgsConstructor를 쓰게 되면 final이 붙은 필드는 롬복이 자동으로 생성자를 만들어주기 때문에 저희가 굳이 추가하지 않아도 됩니다.
@Getter와 @Setter
얘들은 이름 그대로 저희가 Getter와 Setter를 코드에 추가하지 않아도 롬복이 자동으로 만들어주기 때문에 얘도 코드가 엄청 간결해질 수 있습니다.
Member 클래스에서 한번 적용해볼까 죠?
<이전>
package com.naver.shopping.member;
public class Member {
private Long id;
private String name;
private Grade grade;
public Member(Long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
}
<이후>
package com.naver.shopping.member;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Member {
private Long id;
private String name;
private Grade grade;
public Member(Long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
}
(Order 클래스에도 적용해 보세요! Order는 Setter적용은 하면 안 됩니다.)
엄청 간결해졌죠? 이 외에도 롬복이 지원하는 기능들이 많으니 관심 있으신 분들은 공식 사이트를 참조해 보시면 좋을 것 같아요!
추가) 빈이 여러 개일 때 다 가져오는 법
지난 시간에 빈을 조회해서 해당 타입의 빈이 여러 개면 한 개만 가져올 수 있도록 하는 방법을 알려드렸는데요, 근데 만약 다 필요하다면? 예를 들어 저희 프로젝트의 경우 어느 부분에서는 정률, 어느부분에서는 비율 할인 정책을 둘 다 써야 한다면 어떻게 해야 할지 알려드리겠습니다.
autoScanTest로 다시 돌아가봅시다.
@Component
static class OrderService3{
private MemberRepository memberRepository;
private Map<String, DiscountPolicy> discountPolicyMap;
@Autowired
public OrderService3(MemberRepository memberRepository, Map<String, DiscountPolicy> discountPolicyMap) {
this.memberRepository = memberRepository;
this.discountPolicyMap = discountPolicyMap;
}
public Order createOrder(Long memberId, String itemName, int itemPrice, String policyName) {
Member member = memberRepository.findById(memberId);
DiscountPolicy discountPolicy = discountPolicyMap.get(policyName);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
위와 같이 OrderService3도 아래에 추가해 줍시다. createOrder()의 인자값에서 추가를 해야 해서 편의상 OrderService를 implements 하지 않았습니다.
OrderService3에서 바뀐 것은 discountPolicy를 그대로 받는 게 아니라 DiscountPolicy들을 Map형대로 받는다는 것입니다.
그리고 createOrder로 주문을 할 때, 해당 주문이 적용될 할인 정책을 입력받아서 그 정책에 맞게 주문에 따라 바꿀 수 있게 되었습니다.
바로 한번 써봅시다!
@Test
@DisplayName("여러 할인 정책 서비스중 선택")
void getAllDiscountPolicy() {
Member testMember = new Member(1L, "tester", Grade.VIP);
ac.getBean(MemberService.class).join(testMember);
Order fixServiceOrder = ac.getBean(OrderService3.class).createOrder(1L, "아이템1", 20000, "fixDiscountPolicy");
Order rateServiceOrder = ac.getBean(OrderService3.class).createOrder(1L, "아이템1", 20000, "rateDiscountPolicy");
System.out.println("fixServiceOrder = " + fixServiceOrder);
System.out.println("rateServiceOrder = " + rateServiceOrder);
Assertions.assertThat(fixServiceOrder.getDiscountPrice()).isEqualTo(1000);
Assertions.assertThat(rateServiceOrder.getDiscountPrice()).isEqualTo(2000);
}
createOrder의 마지막 부분에 여러 개 잡힌 DiscountPolicy타입의 빈 중 원하는 빈의 이름을 넣어주면 해당 정책으로 할인이 될 것입니다.
실행 결과
각각의 주문별로 할인금액이 잘 들어간 것을 확인하실 수 있습니다.
마치며
오늘은 이렇게 롬복의 편의성과 할인정책을 선택할 수 있는 방법에 대해서도 알아보았습니다.
이제 뭔가 좀 멋있는 기능들도 쓰는 거 같고 만드는 결과물이 느낌이 나지 않나요?
환상적인 프로젝트를 만드는 그날까지 달려봅시다!!!!!
오늘 작업한 내용은 아래 커밋에서 확인하실 수 있습니다.
Use Lombok and add choosing from many beans · KIMB0B/blog_spring@8010e55 (github.com)