반응형
해당 글에서는 이전에 작성한 Lombok 환경 설정 및 이해 글에 이어서 Lombok을 활용하여 심화 활용 기능에 대해 확인합니다.
💡 Lombok 설정 및 기초에 대해서 이해하고 싶은 경우 하단의 이전에 작성한 글을 이용하시면 됩니다.
1) 적용 개발환경
개발 환경 | 버전 |
java | 1.8 |
Spring Boot | 2.7.4 |
빌드관리도구 | Gradle 7.5 |
개발 툴 | IntelliJ IDEA 2022.3 |
Lombok | 1.18.24 |
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
2) Lombok Annotation
💡 Lombok 이란?
- Java 라이브러리로 반복되는 getter, setter, toString 등의 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리입니다. 이는 코드 자체가 반복되는 메서드로 인한 복잡함을 줄여주는 역할을 수행합니다.
어노테이션 | 설명 |
@Getter | 클래스의 모든 필드에 대한 getter 메서드를 생성합니다. |
@Setter | 클래스의 모든 필드에 대한 setter 메서드를 생성합니다. |
@ToString | 클래스의 toString() 메서드를 생성합니다. |
@EqualsAndHashCode | 클래스에 대한 equals() 및 hashCode() 메서드를 생성합니다. |
@NoArgsConstructor | 클래스에 대한 인자가 없는 생성자를 생성합니다. |
@AllArgsConstructor | 클래스의 각 필드에 대해 하나의 매개변수가 있는 생성자를 생성합니다. |
@RequiredArgsConstructor | 클래스의 각 final 필드와 null이 아닌 필드에 대해 하나의 매개변수가 있는 생성자를 생성합니다. |
@Data | 클래스의 모든 필드에 대한 getter 및 setter 메서드, toString() 및 equals() 및 hashCode() 메서드를 생성합니다. |
@Builder | 클래스에 대한 빌더 패턴을 생성합니다. |
@Value | 최종 필드, getter 메서드, toString() 및 equals() 및 hashCode() 메서드가 있는 불변 클래스를 생성합니다. |
@Slf4j | SLF4J 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 생성합니다. |
@Log4j | Log4j 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 생성합니다. |
@Log | 내장 Java 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 생성합니다. |
3) 심화 Annotation 활용하기
💡 해당 부분에서는 기존에 일반적으로 사용되는 Lombok을 확장하여 심화로 활용할 수 있는 방법에 대해서 설명합니다.
1. @NoArgsConstructor Annotation 활용하기
1.1. 문제점 발생
💡 Lombok은 DTO, VO 내에서 생성된 객체에서 데이터를 넣고 빼는 목적으로 @Setter, @Data Annotation을 사용합니다. 해당 Annotation을 이용하여 객체의 '요소 값을 변형하는 기능(Setter)'을 사용합니다.
💡그러나, 해당 Setter의 기능을 사용하면 문제점이 발생합니다.
💡 @Setter 어노테이션을 사용하면 무분별한 객체의 생성과 변형이 발생하여 개발자가 실수로 클래스의 필드 중 하나의 필드에 대한 값 설정을 누락하였을 경우 객체는 불완정한 상태가 되어 버리는 문제가 발생합니다.
💡 [문제점 확인 -1 ]
- 아래와 같이 객체를 생성하고 값을 setter로 지정하였으나 ‘지정된 값을 다시 대입’ 하는 오류를 범할 수도 있으며 개발자의 실수로 ‘값의 누락’이 발생할 수 있는 객체 불완정 상태가 발생할 수 있습니다.
package com.adjh.multiflexapi.model;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
}
[문제점 확인 -2 : 생성자를 통한 객체 생성]
- 필요에 따라서 ‘매번 생성자를 구성’해야하는 점과 IDE 툴에서 각각의 ‘파라미터명을 지원해주지 않는다면 잘못된 값’을 넣을 수 있다는 오류를 범할 수 있습니다.
package com.adjh.multiflexapi.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
public TestDto(int userSq, String userId, String userPw, String userNm, String userSt) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
}
}
1.2. 해결 방안
💡 Lombok의 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 이용하여 이를 해결합니다.
💡 @NoArgsConstructor(access = AccessLevel.PROTECTED) 란?
- ‘파라미터가 없는 생성자’를 생성해주는 어노테이션이며 무분별한 객체 생성에 대해서 한번 더 체크할 수 있는 수단입니다.
- @NoArgsConstructor 어노테이션을 통해 파라미터가 없는 생성자를 생성을 제한합니다.
- AccessLevel.PROTECTED 속성으로 생성자를 같은 패키지 내에서, 또는 해당 클래스를 상속받은 하위 클래스에서만 접근 가능하도록 제한합니다.
- 즉, 결론적으로 생성자 사용을 금지하며 패키지내에서만 접근이 가능하도록 구성하는것입니다.
Access 속성 | 설명 |
AccessLevel.PUBLIC | 생성자를 모든 클래스에서 접근 가능하도록 설정합니다. (기본값) |
AccessLevel.PROTECTED | 생성자를 같은 패키지 내에서, 또는 해당 클래스를 상속받은 하위 클래스에서만 접근 가능하도록 설정합니다. |
AccessLevel.PRIVATE | 생성자를 해당 클래스 내에서만 접근 가능하도록 설정합니다. |
💡 아래와 같이 지정을 하면 객체 생성 및 객체 요소들의 값의 대입이 불가능합니다.
💡 이에 대해 해결 방안으로 아래의 @Builder Annotation을 사용하여 구성하여 무분별한 객체 생성에 대해서 한번 더 체크할 수 있습니다.
💡 해당 해결은 하단의 2번에서 이어집니다.
package com.adjh.multiflexapi.model;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TestDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
}
2. @Builder Annotation 활용하기
2.1. 문제점 발생
💡 위에서 언급한 점층적 생성자 패턴이나 자바 빈즈 패턴을 이용하여서 생성자를 구성하는 경우 무분별한 객체를 생성하여 값이 누락이 될 수 있는 문제점을 가지고 있습니다.
💡 이에 따라서 ‘빌더 패턴’을 이용하여서 객체의 생성자를 생성하는 방법을 이용합니다.
[참고] 이전에 작성한 글에서 각각에 대한 이해를 도울 수 있습니다.
2.2. 해결 방안
💡 빌더 패턴을 이용하면 하나의 객체의 값들을 구성하고자 할 때 각각의 생성자를 구성하는 것이 아니라 @Builder만 구성하여 객체를 생성할 때 필요한 값만 넣어주고 객체를 구성합니다. 이를 통해 객체 내의 요소값들을 한눈에 확인이 가능하며, 매번 필요에 따라 생성자를 만들어 줄 필요도 없습니다.
[ 다시 알아보기 ]
💡 빌더 패턴(Builder Pattern)이란?
- 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴을 의미합니다.
- 다시 말해, Lombok에서 제공해 주는 Annotation으로 별도의 Bulider Pattern을 구성할 필요 없이 생성자에 @Bulider를 선언하면 이를 자동으로 구성하는 것을 의미합니다.
package com.adjh.multiflexapi.model;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TestDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
@Builder
TestDto(int userSq, String userId, String userPw, String userNm, String userSt) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
}
}
💡 [참고] @Builder의 속성
어노테이션 속성 | 설명 |
builderClassName | 빌더 클래스의 이름을 지정합니다. |
builderMethodName | 빌더 인스턴스를 얻는 데 사용할 메서드 이름을 지정합니다. |
buildMethodName | 클래스의 인스턴스를 빌드하는 데 사용할 메서드 이름을 지정합니다. |
toBuilder | 기존 클래스 인스턴스를 기반으로 빌더 인스턴스를 얻는 빌더 메서드를 생성합니다. |
access | 빌더 클래스 및 생성자의 가시성을 지정합니다. |
setterPrefix | 생성된 설정기 메서드에 사용되는 접두사를 변경합니다. |
fluent | 빌더 메서드에 대해 유창한 API 스타일을 활성화합니다. |
generateBuilderPackage | 생성된 빌더 클래스에 사용할 패키지 이름을 지정합니다. |
generateBuilderClassName | 생성된 빌더 클래스에 사용할 이름을 지정합니다. |
useConstructorProperties | 도구 지원에 도움이 되는 @ConstructorProperties 어노테이션 사용을 활성화합니다. |
onConstructor | 생성된 생성자의 접근 수준을 지정합니다. |
onMethod | 생성된 빌더 메서드의 접근 수준을 지정합니다. |
onParam | 생성된 빌더 메서드 매개변수의 접근 수준을 지정합니다. |
anyConstructor | 특정 생성자 대신 빌더가 어떤 생성자를 사용할지 지정합니다. |
disableBuilder | 빌더 클래스의 생성을 비활성화합니다. |
toBuilderMethodName | 기존 클래스 인스턴스를 기반으로 빌더 인스턴스를 얻는 데 사용할 메서드 이름을 지정합니다. |
[참고] 추가 Builder 패턴에 대한 심화 속성 사용에 대해서 궁금하시면 아래의 링크를 이용하시면 됩니다.
3. @Slf4J / @Log4J Annotation 활용하기
3.1. 문제점 발생
💡 구성한 로깅 라이브러리를 사용하는 모든 페이지마다 Logger를 선언해주어야 하는 번거로움이 발생합니다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jLogginSample {
private static final Logger log = LoggerFactory.getLogger(Slf4jLogginSample.class);
public static void main(String[] args) {
log.info("Slf4jLogginSample");
}
}
3.2. 해결 방안
💡 설정된 로깅 라이브러리를 기준으로 매번 선언의 필요 없이 @Slf4j, @Log4j을 통해 선언을 하면 매번 로깅 라이브러리의 선언 필요 없이 쉽게 어노테이션 설정만으로 사용이 가능합니다.
어노테이션 | 설명 |
@Slf4j | SLF4J 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 생성합니다. |
@Log4j | Log4j 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 생성합니다. |
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Slf4jforLombok {
public static void main(String[] args) {
log.info("Slf4j 테스트");
}
}
[ 더 알아보기 ]
💡 spring-boot-starter-log4j2의 로깅 라이브러리 설정 없이 @Slf4j, @Log4j를 사용할 수 있는가?
- 서버의 콘솔 내에 출력만 사용을 한다면 해당 Annotation을 사용하면 되지만 실제 ‘로그의 기록’을 하려면 해당 로깅 프레임워크의 설정이 필요합니다.
💡 @Slf4j, @Log4j 어노테이션과 spring-boot-starter-log4j2의 관계는 무슨 관계인가?
- @Slf4j, @Log4j는 로깅 프레임워크를 사용하여 클래스에 대한 로거 인스턴스를 자동으로 생성합니다.
- spring-boot-starter-log4j2 는 Log4j2를 사용하기 위한 라이브러리입니다.
- 로그를 기록하기 위해서는 @Slf4j, @Log4j와 spring-boot-starter-log4j2 라이브러리를 함께 사용해야 기록을 할 수 있습니다.
[참고] 로깅 라이브러리 Log4j2 설정 방법 + Slf4j 인터페이스 이용방법
오늘도 감사합니다. 😀
반응형
'Java > 아키텍처 & 디자인 패턴' 카테고리의 다른 글
[Java] 개발 환경에 따라 각각 환경 파일 구성 방법: application.properties (0) | 2023.06.07 |
---|---|
[Java] 스레드(Thread) 이해하기 -1 : 구조, 상태, 예시 (0) | 2023.04.17 |
[Java] RESTful API 설계 방법 -2 : 구성하기 (0) | 2023.03.26 |
[Java] RESTful API 설계 방법 -1 : 이해하기 (0) | 2023.03.22 |
[Java] 계층화된 아키텍처(Layered Architecture) : N Tier Architecture (0) | 2023.01.25 |