[첫 사이드프로젝트 도전기] Dependency들 정리하기 | Spring Boot Devtools, Lombok

Minju Kim·2024년 9월 1일
0

사이드프로젝트

목록 보기
3/9
post-thumbnail

많이는 못 하고,,(JPA 등은 내용이 너무 방대해서 따로 포스트로 뺄 예정 - 노션에 전부 정리하고 일단~)

그래서 어떻게 공부했냐 하면..? 공식 문서 들어가서 싹 긁어와서 챗지피티한테 번역 시켜서 질문 하는 식으로 했습니다..

✓ Spring Boot Devtools

Spring Boot Devtools

Spring Boot DevTools는 개발자의 편의를 위한 추가 기능을 제공하는 도구 모음입니다. 이 의존성을 프로젝트에 추가하면 개발 중에 다양한 유용한 기능들을 사용할 수 있습니다. spring-boot-devtools 의존성을 사용하면 자동으로 캐시 비활성화, 애플리케이션 자동 재시작, 라이브 리로드(LiveReload) 등을 통해 개발 생산성을 크게 향상시킬 수 있습니다.

1. 의존성 추가

Spring Boot DevTools를 사용하기 위해서는 spring-boot-devtools 의존성을 프로젝트에 추가해야 합니다. Maven과 Gradle을 사용하는 경우 각각 다음과 같이 의존성을 추가할 수 있습니다:

  • Maven:
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
  • Gradle:
    dependencies {
        compileOnly("org.springframework.boot:spring-boot-devtools")
    }
    

참고: spring-boot-devtools 의존성은 애플리케이션이 완전히 패키징되어 실행되는 경우 자동으로 비활성화됩니다. 이를 통해 프로덕션 환경에서는 개발 도구들이 불필요하게 작동하지 않도록 방지합니다.

2. 주요 기능

2.1 자동 재시작

Spring Boot DevTools는 클래스패스에 있는 파일이 변경될 때마다 애플리케이션을 자동으로 재시작합니다. 이는 코드 변경 후 IDE에서의 빠른 피드백 루프를 제공하는 데 매우 유용합니다. 예를 들어, Eclipse에서는 파일을 저장할 때, IntelliJ IDEA에서는 프로젝트를 빌드할 때 재시작이 트리거됩니다.

참고: JRebel과 같은 다른 클래스 리로딩 도구를 사용하는 경우 Spring Boot DevTools의 자동 재시작 기능이 비활성화되며, 대신 해당 도구의 동적 클래스 리로딩 기능이 사용됩니다.

2.2 캐시 비활성화

Spring Boot DevTools는 개발 시 생산성을 저하할 수 있는 캐싱 기능들을 기본적으로 비활성화합니다. 예를 들어, 템플릿 엔진(Thymeleaf 등)은 템플릿 파일을 반복적으로 파싱하는 것을 방지하기 위해 템플릿을 캐시합니다. 하지만 개발 중에는 이러한 캐시가 변경 사항을 즉시 확인하지 못하게 할 수 있어, DevTools가 이러한 캐시를 자동으로 비활성화합니다.

2.3 라이브 리로드(LiveReload)

DevTools에는 내장된 LiveReload 서버가 포함되어 있어, 리소스가 변경될 때 브라우저를 자동으로 새로 고침할 수 있습니다. 브라우저에 LiveReload 확장 프로그램을 설치하면 이러한 기능을 사용할 수 있습니다.

2.4 원격 애플리케이션 지원

DevTools는 로컬 개발에만 국한되지 않고, 원격 애플리케이션에서도 사용할 수 있습니다. 이를 위해 spring.devtools.remote.secret 속성을 설정하여 원격 지원을 활성화할 수 있습니다. 하지만 이 기능은 보안상 위험이 있으므로 프로덕션 환경에서는 절대 활성화하지 말아야 합니다.

2.5 기타 기능

  • 경로 감시 및 재시작 설정: 특정 파일이나 경로를 변경할 때 재시작을 트리거하지 않도록 제외하거나, 추가 감시 경로를 설정할 수 있습니다.
  • 재시작 비활성화: 재시작 기능이 필요하지 않거나 특정 라이브러리와 호환되지 않는 경우 재시작 기능을 비활성화할 수 있습니다.
  • 트리거 파일 사용: 특정 시점에서만 재시작을 트리거하고 싶다면 "트리거 파일"을 사용할 수 있습니다.

Spring Boot DevTools는 이러한 다양한 기능을 통해 개발자에게 더 나은 개발 경험을 제공합니다. 각 기능은 개발 환경에 맞게 적절히 설정하여 최상의 생산성을 낼 수 있습니다.

✓ Lombok

프로젝트 롬복(Project Lombok) 소개

  • *프로젝트 롬복(Project Lombok)**은 자바에서 반복적으로 작성해야 하는 "보일러플레이트 코드(boilerplate code)"를 줄여주는 도구입니다. 자바는 다른 언어에 비해 반복적인 코드가 많이 발생하는 경향이 있는데, Lombok은 이를 간단한 애노테이션으로 대체해 코드를 더 간결하고 유지보수하기 쉽게 만들어줍니다.

롬복 설치

Lombok은 공식 사이트에서 제공되는 JAR 파일로 설치할 수 있습니다. 설치 과정은 간단합니다. JAR 파일을 실행하면 IDE에 통합할 수 있는 설치 프로그램이 실행됩니다. 설치 후에는 프로젝트에서 Lombok 애노테이션을 사용할 수 있게 됩니다. 예를 들어, Maven 프로젝트에서 Lombok을 사용하려면 pom.xml에 다음과 같은 의존성을 추가하면 됩니다:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>0.9.2</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>projectlombok.org</id>
        <url><http://projectlombok.org/mavenrepo></url>
    </repository>
</repositories>

롬복 애노테이션 사용법

@Getter와 @Setter

이 애노테이션은 필드에 대해 getter와 setter 메서드를 자동으로 생성해줍니다. 자바에서 일반적으로 사용되는 접근자 메서드를 Lombok이 자동으로 생성해줌으로써, 이를 일일이 작성할 필요가 없어집니다.

@Getter @Setter
private boolean employed = true;

@Setter(AccessLevel.PROTECTED)
private String name;

위 코드에서 employed 필드에 대한 isEmployed()setEmployed() 메서드가 자동으로 생성되며, name 필드에 대한 setName() 메서드가 protected 접근 수준으로 생성됩니다.

@NonNull

@NonNull 애노테이션은 필드에 null 값이 할당될 수 없음을 명시합니다. 이 애노테이션이 적용된 필드에 null 값이 설정되면, 자동으로 NullPointerException이 발생하도록 설정됩니다.

@Getter @Setter @NonNull
private List<Person> members;

이 코드에서는 members 필드에 null 값이 할당되려 할 때 NullPointerException이 발생합니다. 또한 Lombok이 생성하는 생성자에서도 이 필드를 포함하며, 생성 시에도 null 체크를 수행합니다.

@ToString

@ToString 애노테이션은 클래스의 toString() 메서드를 자동으로 생성해줍니다. 기본적으로 모든 비정적 필드를 포함하며, 필드 이름과 값을 쌍으로 출력합니다.

@ToString(callSuper=true, exclude="someExcludedField")
public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
}

위 코드에서 toString() 메서드는 someExcludedField를 제외한 나머지 필드를 포함한 문자열을 반환하며, 상위 클래스의 toString() 결과도 포함됩니다.

@EqualsAndHashCode

@EqualsAndHashCode 애노테이션은 클래스의 equals()hashCode() 메서드를 자동으로 생성해줍니다. 이 메서드는 모든 비정적 필드를 기준으로 동등성을 판단하고 해시코드를 계산합니다.

@EqualsAndHashCode(callSuper=true, exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    @NonNull private String name;
    @NonNull private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}

위 코드에서는 address, city, state, zip 필드를 제외한 나머지 필드를 기반으로 equals()hashCode() 메서드가 생성됩니다. 또한, 상위 클래스의 메서드도 호출하여 동등성 검사를 수행합니다.

무슨 소리임?

@EqualsAndHashCode(callSuper=true, exclude={"address","city","state","zip"})는 Lombok의 @EqualsAndHashCode 애노테이션을 사용하여 equals()hashCode() 메서드를 자동으로 생성할 때 사용하는 설정입니다. 이 애노테이션에 포함된 각 옵션은 다음과 같은 의미를 가집니다:

  1. callSuper=true:
    • 이 옵션은 equals()hashCode() 메서드를 생성할 때, 상위 클래스의 equals()hashCode() 메서드도 호출하도록 설정하는 것입니다. 즉, 상속 관계에 있는 클래스에서 부모 클래스가 가진 필드들도 equals()hashCode() 메서드에서 비교하고 계산하도록 합니다. 이를 통해 상위 클래스에서 정의된 필드의 동등성도 함께 고려할 수 있습니다.
  2. exclude={"address","city","state","zip"}:
    • 이 옵션은 equals()hashCode() 메서드를 생성할 때, 특정 필드들을 비교 대상에서 제외시키는 역할을 합니다. 여기서는 address, city, state, zip 필드가 제외됩니다. 즉, 이 필드들은 equals()hashCode() 메서드에서 고려되지 않으며, 객체의 동등성 판단이나 해시코드 계산에 포함되지 않습니다.

이러한 설정을 사용하면, Person 클래스와 같이 많은 필드를 가진 클래스에서 불필요한 필드를 equals()hashCode()에서 제외하거나, 상위 클래스의 필드를 비교 대상에 포함시킬 수 있어 유연하게 메서드를 생성할 수 있습니다.

예시 코드

@EqualsAndHashCode(callSuper=true, exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    @NonNull private String name;
    @NonNull private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}

위 코드에서 Person 클래스는 SentientBeing이라는 부모 클래스를 상속받고 있습니다. @EqualsAndHashCode 애노테이션은 이 클래스에서 equals()hashCode() 메서드를 자동으로 생성합니다.

  • callSuper=true 설정 덕분에, SentientBeing 클래스의 equals()hashCode() 메서드도 호출되어 Person 객체의 동등성을 판단할 때 SentientBeing의 필드들도 비교됩니다.
  • 반면에, address, city, state, zip 필드는 동등성 판단에서 제외됩니다.

이로 인해, Person 객체를 비교할 때 주소 관련 정보는 무시하고, 이름, 성별 등 중요한 필드들만을 비교하게 됩니다.

EqualsAndHashCode 이게 도대체 뭐야 hashcode가 뭔데?

@EqualsAndHashCode란?

@EqualsAndHashCode는 Lombok에서 제공하는 애노테이션으로, 자바 클래스에 equals()hashCode() 메서드를 자동으로 생성해주는 기능을 제공합니다. 이 두 메서드는 자바 객체의 동등성(equality)과 해시 코드(hash code) 관련 작업에서 매우 중요한 역할을 합니다.

equals() 메서드란?

equals() 메서드는 두 객체가 논리적으로 같은지를 비교하는 메서드입니다. 기본적으로 Object 클래스에 정의되어 있으며, 이 메서드를 오버라이드(재정의)하여 객체의 특정 필드들이 같은지를 비교할 수 있습니다. 예를 들어, 두 Person 객체가 있을 때, 이 객체들의 이름과 나이가 같다면 두 객체를 동일한 것으로 간주할 수 있습니다.

@Override
public boolean equals(Object obj) {
    if (this == obj) return true; // 같은 객체를 가리키면 true
    if (obj == null || getClass() != obj.getClass()) return false; // null 또는 다른 클래스이면 false
    Person person = (Person) obj;
    return age == person.age && name.equals(person.name); // 논리적 비교
}

hashCode() 메서드란?

hashCode() 메서드는 객체의 해시 코드를 반환하는 메서드로, 해시 기반 컬렉션(예: HashMap, HashSet)에서 객체를 빠르게 검색하거나 저장할 때 사용됩니다. 자바의 Object 클래스는 기본적으로 이 메서드를 제공하지만, 의미 있는 방식으로 객체를 비교하기 위해서는 이 메서드를 오버라이드해야 합니다.

해시 코드는 같은 내용을 가진 객체들이 동일한 해시 코드를 반환하도록 정의되어야 합니다. 이는 equals() 메서드를 오버라이드하는 경우 hashCode()도 함께 오버라이드해야 한다는 것을 의미합니다.

@Override
public int hashCode() {
    return Objects.hash(name, age); // 객체의 필드들로 해시코드 생성
}

@EqualsAndHashCode의 역할

Lombok의 @EqualsAndHashCode 애노테이션을 사용하면, 위에서 설명한 equals()hashCode() 메서드를 직접 작성하지 않아도 자동으로 생성해줍니다. 이 애노테이션을 클래스에 붙이면, 클래스의 모든 필드를 기준으로 equals()hashCode() 메서드가 생성됩니다.

예를 들어:

@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

위 코드를 작성하면 Lombok은 nameage 필드를 기반으로 equals()hashCode() 메서드를 자동으로 생성합니다.

hashCode()equals()의 중요성

  • 데이터의 무결성: 해시 기반의 자료구조에서 hashCode()equals()는 중요한 역할을 합니다. 예를 들어, HashMap에서 키 객체의 해시 코드를 이용해 빠르게 값에 접근하는데, 해시 코드가 같으면 equals()를 통해 실제로 같은 객체인지 확인합니다.
  • 효율성: 잘 설계된 hashCode()는 해시 기반 자료구조의 성능을 최적화하는 데 도움이 됩니다. 서로 다른 객체들이 같은 해시 코드를 갖게 되면 충돌이 발생할 수 있어, 이 경우 equals()를 추가로 호출하게 되므로, 해시 코드의 분포가 잘 되어 있어야 효율적입니다.

정리

  • equals(): 두 객체가 논리적으로 같은지 비교하는 메서드.
  • hashCode(): 객체를 해시 기반 컬렉션에서 빠르게 검색할 수 있도록 하는 해시 코드 값을 반환하는 메서드.
  • @EqualsAndHashCode: Lombok이 제공하는 애노테이션으로, 클래스의 모든 필드를 기준으로 equals()hashCode() 메서드를 자동으로 생성해주는 도구.

이를 통해 코드 작성이 간결해지며, 보일러플레이트 코드를 줄이고 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

해시 기반 콜렉션이 뭐임? 해시가 뭐야?

해시 기반 컬렉션(Hash-Based Collections)이란?

해시 기반 컬렉션은 해시 함수를 이용해 데이터를 저장하고 검색하는 자바의 컬렉션 클래스입니다. 이러한 컬렉션들은 효율적인 데이터 검색과 저장을 가능하게 해주며, 대표적인 예로는 HashMap, HashSet, LinkedHashMap, LinkedHashSet 등이 있습니다.

해시(Hash)란?

  • *해시(Hash)*는 데이터를 특정 길이의 고정된 값을 가지는 해시 코드로 변환하는 과정입니다. 이때 사용되는 함수가 해시 함수(Hash Function)입니다. 해시 함수는 임의의 크기의 데이터를 입력받아, 일정한 크기의 고정된 길이 값을 출력합니다. 이 고정된 값을 해시 코드(Hash Code) 또는 해시 값(Hash Value)라고 부릅니다.

해시 함수의 특징

  • 고속성: 해시 함수는 데이터를 해시 코드로 변환하는 과정이 매우 빠릅니다.
  • 고정된 크기: 입력 데이터의 크기에 관계없이 해시 함수의 출력은 일정한 크기입니다.
  • 충돌(Collision): 서로 다른 두 데이터가 동일한 해시 코드를 갖는 경우가 발생할 수 있습니다. 이를 충돌이라고 합니다. 좋은 해시 함수는 이러한 충돌을 최소화하는 것이 목표입니다.

해시 기반 컬렉션의 작동 원리

해시 기반 컬렉션은 해시 코드를 이용해 데이터를 효율적으로 저장하고 검색합니다. 데이터가 저장될 때, 해시 함수는 데이터를 해시 코드로 변환하고, 이 해시 코드를 이용해 데이터를 저장할 위치(버킷, bucket)를 결정합니다.

예를 들어, HashMap에서 데이터를 저장하거나 검색할 때의 과정은 다음과 같습니다:

  1. 저장:
    • HashMap에 데이터를 저장할 때, 키(key)에 대해 해시 함수가 적용되어 해시 코드가 생성됩니다.
    • 이 해시 코드에 따라 해당 데이터가 저장될 버킷(메모리 공간)이 결정됩니다.
    • 동일한 해시 코드를 가진 데이터가 이미 있을 경우 충돌이 발생하며, 이 경우 해당 버킷 내에서 연결 리스트나 트리 구조를 통해 데이터를 추가로 저장합니다.
  2. 검색:
    • 데이터를 검색할 때도 동일한 해시 함수가 사용되어 해시 코드가 생성됩니다.
    • 이 해시 코드에 따라 해당 데이터가 저장된 버킷을 빠르게 찾습니다.
    • 충돌이 발생한 경우, 연결 리스트나 트리 구조에서 추가로 검색하여 원하는 데이터를 찾습니다.

예시: HashMap

HashMap은 키-값 쌍으로 데이터를 저장하는 해시 기반 컬렉션입니다.

import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();

        // 데이터 삽입
        map.put("apple", 10);
        map.put("banana", 15);
        map.put("cherry", 20);

        // 데이터 검색
        int value = map.get("banana");
        System.out.println("Value for 'banana': " + value);
    }
}

이 예제에서 "banana" 키에 대해 해시 함수가 해시 코드를 생성하고, 해당 해시 코드에 기반해 "banana"에 해당하는 값을 HashMap에서 빠르게 검색해 출력합니다.

해시 기반 컬렉션의 장점

  • 빠른 검색: 해시 기반 컬렉션은 평균적으로 O(1) 시간 복잡도로 데이터를 검색할 수 있습니다. 이는 리스트나 배열처럼 순차적으로 데이터를 검색해야 하는 구조보다 훨씬 빠릅니다.
  • 효율적인 메모리 사용: 해시 기반 컬렉션은 메모리를 효율적으로 사용하면서도, 빠른 검색을 가능하게 합니다.

해시 기반 컬렉션의 단점

  • 충돌: 해시 함수가 다른 입력에 대해 동일한 해시 코드를 생성하는 경우 충돌이 발생합니다. 충돌이 많아지면 성능이 저하될 수 있습니다.
  • 순서 보장 없음: 일반적으로 해시 기반 컬렉션은 데이터의 저장 순서를 보장하지 않습니다. 다만, LinkedHashMap과 같은 클래스는 순서를 유지합니다.

정리

  • 해시(Hash): 데이터를 고정된 길이의 해시 코드로 변환하는 과정.
  • 해시 기반 컬렉션: 해시 코드를 이용해 데이터를 저장하고 검색하는 컬렉션 (HashMap, HashSet 등).
  • 장점: 빠른 데이터 검색과 효율적인 메모리 사용.
  • 단점: 충돌 가능성과 데이터 순서 보장 문제.

해시 기반 컬렉션은 자바에서 매우 중요한 역할을 하며, 효율적인 데이터 처리를 위해 널리 사용됩니다.

@Data

@Data 애노테이션은 가장 자주 사용되는 Lombok 애노테이션 중 하나로, @ToString, @EqualsAndHashCode, @Getter, @Setter를 한 번에 적용해줍니다. 이를 통해 간단한 데이터 클래스(POJO)를 작성할 때 매우 유용합니다.

@Data(staticConstructor="of")
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
}

이 코드에서 @Data 애노테이션은 클래스의 모든 필드에 대해 getter, setter, toString, equals, hashCode 메서드를 자동으로 생성합니다. 또한 staticConstructor="of"를 사용하여 정적 팩토리 메서드를 생성합니다.

롬복의 장점과 한계

장점:

  • 코드가 간결해져 가독성이 좋아집니다.
  • 보일러플레이트 코드가 줄어들어 유지보수가 쉬워집니다.
  • 개발 생산성이 향상됩니다.

한계:

  • IDE나 JDK 업데이트로 인해 호환성 문제가 발생할 수 있습니다.
  • 특정 상황에서는 @SneakyThrows와 같은 애노테이션이 논란을 일으킬 수 있습니다.
  • 일부 애노테이션은 자바 언어 자체가 개선되면 더 이상 필요하지 않을 수 있습니다.

Lombok은 개발자에게 강력한 도구가 될 수 있지만, 그 사용에는 신중한 고려가 필요합니다. 프로젝트의 요구 사항에 따라 Lombok이 제공하는 혜택이 단점보다 크다면, 이를 도입하여 코드의 품질을 높일 수 있습니다.

0개의 댓글