스프링 개발 필수 롬복(Lombok) 활용법과 실무 주의점

💡 핵심 요약 (Executive Summary)

본 가이드는 fw spring Lombok 핵심 가이드 및 실전 아키텍처의 핵심 개념, 실전 구성 명령어 및 아키텍처 베스트 프랙티스를 다룹니다. 클라우드 네이티브 및 인프라 엔지니어링 실무에 즉시 활용 가능하도록 체계적으로 정리했습니다.

📑 Table of Contents
Common
Annotation

Class에 @Data 애노테이션을 추가하면 toString() 등 활용도 높은 메서드를 사용할 수 있다.

sql
@Data //아래의 모든 애노테이션을 포함하는 문법
//	@Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor

@NoArgsConstructor //파라미터가 없는 기본생성자 자동 선언
@AllArgsConstructor //모든 파라미터를 명시하는 기본생성자 자동 선언
@RequiredArgsConstructor //final이나 @NonNull인 필드 값만 파라미터로 받는 생성자 만듦
Method
sql
public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;
  
  public DataExample(String name) {
    this.name = name;
  }
  
  public String getName() {
    return this.name;
  }
  
  void setAge(int age) {
    this.age = age;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public void setScore(double score) {
    this.score = score;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  public void setTags(String[] tags) {
    this.tags = tags;
  }
  
  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof DataExample;
  }
  
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  public static class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Exercise;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}
Feature
@RequiredArgsConstructor: 인터페이스 구현시 의존성 주입 자동화
GPT

> 💡
\[질문\]

@RequiredArgsConstructor
public class JobService \{
private final Job calculateServiceInstanceRunningTimeJob;
private final Job serviceInstanceStateCheckJob;
서비스가 이런식으로 정의되었다면 기본적으로 의존성 주입을 하여

public class CalculateServiceInstanceRunningTimeJob implements Job 클래스를 calculateServiceInstanceRunningTimeJob에 할당하고,

public class ServiceInstanceStateCheckJob implements Job 클래스를 serviceInstanceStateCheckJob에 할당할것이라고 보아도 되겠는가?

\[답변\]

네, 맞습니다. @RequiredArgsConstructor가 적용된 클래스에서는 해당 클래스의 final 필드를 매개변수로 받는 생성자가 자동으로 생성됩니다. 이 생성자는 주입 받은 매개변수에 따라 해당 필드에 값을 할당하게 됩니다.

따라서, 예를 들어 다음과 같은 코드가 있다면:

java
javaCopy code
@RequiredArgsConstructor
public class JobService {
    private final Job calculateServiceInstanceRunningTimeJob;
    private final Job serviceInstanceStateCheckJob;
    // ...
}

Spring의 빈으로 등록된 JobService 클래스는 이 두 필드를 생성자를 통해 주입받습니다. 이때, Spring은 각 필드의 타입을 보고 알맞은 빈을 찾아서 주입하게 됩니다. 따라서 calculateServiceInstanceRunningTimeJob 필드에는 CalculateServiceInstanceRunningTimeJob 클래스의 인스턴스가, 그리고 serviceInstanceStateCheckJob 필드에는 ServiceInstanceStateCheckJob 클래스의 인스턴스가 주입될 것입니다.

– 시사하는 점
– Bean 등록시 Default Qualifier
– Class명 변수명 (소문자 시작)
– (수동) Bean 등록
– @Component
– (수동) Service Constructor

java
@Autowired
        public MyService(@Qualifier("implementationA") MyInterface implementationA,
                         @Qualifier("implementationB") MyInterface implementationB) {
            this.implementationA = implementationA;
            this.implementationB = implementationB;
        }

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *