Lombok 이란?

  • Java 프로젝트에서 보일러플레이트(boilerplate) 코드
    즉, 반복적으로 작성해야하는 코드를 줄이기 위해 사용되는 라이브러리이다.
  • 코드의 가독성을 높이고 생산성이 향상된다.

주요 기능

1. Getter / Setter 자동 생성

  • @Getter / @Setter를 사용하지 않는다면,
더보기
public class User {
    private String name;
    private int age;

    // Getter for name
    public String getName() {
        return name;
    }

    // Setter for name
    public void setName(String name) {
        this.name = name;
    }

    // Getter for age
    public int getAge() {
        return age;
    }

    // Setter for age
    public void setAge(int age) {
        this.age = age;
    }
}
  • 클래스의 필드에 대해 getter / setter 메서드를 자동으로 생성
  • 어노테이션 : @Getter, @Setter
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {
    private String name;
    private int age;
}

2. 생성자 자동 생성

  • @NoArgsConstructor, @AllArgsContructor, @RequiredArgsConstructor을 사용하지 않는다면,
더보기
public class User {
    // 필드 정의
    private String name;
    private int age;

    // 기본 생성자 (No-Args Constructor)
    public User() {
    }

    // 모든 필드를 초기화하는 생성자 (All-Args Constructor)
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Service {
    private final String serviceName;
    private final int serviceId;
    private String description; // final이 아님

    // RequiredArgsConstructor에 해당하는 생성자
    public Service(String serviceName, int serviceId) {
        this.serviceName = serviceName;
        this.serviceId = serviceId;
    }
}
  • 다양한 생성자를 자동으로 생성
  • 어노테이션 : @NoArgsConstructor, @AllArgsContructor, @RequiredArgsConstructor
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private int age;
}
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class Service {
    private final String serviceName;
    private final int serviceId;
    private String description; // final이 아님, 생성자에 포함되지 않음
}

 

3. toString() 메서드 자동 생성

  • @ToString을 사용하지 않는다면,
더보기
public class User {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 객체의 문자열 표현을 자동 생성
  • 어노테이션 : @ToString
import lombok.ToString;

@ToString
public class User {
    private String name;
    private int age;
}

 

4. hashCode() 와 equals() 자동 생성

  • @EqualsAndHashCode을 사용하지 않는다면,
더보기
public class Person {
    private String name;
    private int age;

    // hashCode() implementation
    @Override
    public int hashCode() {
        int result = 17; // Initial non-zero prime
        result = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + age;
        return result;
    }

    // equals() implementation
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // Check reference equality
        if (obj == null || getClass() != obj.getClass()) return false; // Check null or type

        Person other = (Person) obj; // Safe cast
        if (age != other.age) return false; // Check primitive field
        return (name != null ? name.equals(other.name) : other.name == null); // Check object field
    }
}
  • 객체 비교에 필요한 메서드를 자동 생성
  • 어노테이션 : @EqualsAndHashCode
import lombok.EqualsAndHashCode;

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

 

5. 빌더 패턴 생성

  • 객체 생성 시 빌더 패턴을 쉽게 사용할 수 있도록 지원합니다.
  • @Builder를 사용하지 않는다면,
더보기
public class User {
    private String name;
    private int age;

    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    public static class Builder {
        private String name;
        private int age;

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

// 빌더 패턴 사용 예제
User user = User.builder()
                .name("John")
                .age(30)
                .build();
  • 어노테이션 : @Builder
import lombok.Builder;

@Builder
public class User {
    private String name;
    private int age;
}

User user = User.builder()
                .name("John")
                .age(30)
                .build();

 

6. 로그 로깅 지원

  • 다양한 로깅 라이브러리에 대해 자동으로 로그 객체를 생성
  • @Slf4j, @Log, @Log4j2
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class User {
    public void logExample() {
        log.info("Logging with Lombok");
    }
}

Lombok 장점

  • 코드 간소화
    • 보일러플레이트 코드를 줄여 코드 가독성과 유지보수성을 높임
  • 생산성 향상
    • 코드 작성 시간을 줄이고 주요 비즈니스 로직에 집중
  • 가독성 향상
    • 필요 없는 코드를 제거하여 클래스의 핵심 내용만 확인

 

Lombok 단점

  • 코드 디버깅 어려움
    • Lombok이 생성한 메서드나 필드는 코드에 직접 표시되지 않아
      디버깅이 어려움
  • 의존성 추가 필요
    • Lombok을 사용하려면 프로젝트에 Lombok 의존성을 추가해야 함
  • IDE 설정 필요
    • 일부 IDE에서는 Lombok 플러그인을 설치해야 제대로 동작
      • eclipse
      • Intellij

 

반응형

'Spring Framework' 카테고리의 다른 글

[Spring] JPA vs MyBatis  (0) 2024.12.03
[Spring] Dispatcher Servlet  (2) 2024.11.22
[Spring] Maven vs Gradle  (3) 2024.11.19
[Spring] 정적 팩토리 메서드 (Static Factory Method)  (2) 2024.11.14

JPA(Java Persistence API)와 MyBatis는 둘 다 데이터베이스와 애플리케이션 간의 데이터 접근을 쉽게 하기 위한 프레임워크입니다. 하지만 그 작동 방식과 특징이 매우 다릅니다. 아래에서 JPA와 MyBatis의 주요 차이점, 장단점, 그리고 어떤 상황에서 사용하면 좋은지 정리하겠습니다.


1. JPA (Java Persistence API)

특징

  1. ORM (Object-Relational Mapping) 기반
    • 객체와 테이블을 매핑하여 SQL을 작성하지 않고도 데이터베이스와 상호작용 가능.
    • Hibernate, EclipseLink 등이 JPA 구현체로 사용됨.
  2. JPQL (Java Persistence Query Language)
    • SQL과 유사한 언어로 엔티티 객체를 대상으로 쿼리 작성.
    • 데이터베이스 독립성을 유지하면서 동작.
  3. 자동화된 데이터 관리
    • 데이터 저장, 갱신, 삭제, 조회를 엔티티 객체로 처리.
    • 변경 감지(Dirty Checking), 1차 캐시, 연관관계 매핑, 지연 로딩(Lazy Loading) 같은 기능 지원.
  4. 추상화
    • 비즈니스 로직에서 SQL을 숨기고 객체 지향적 프로그래밍 가능.

장점

  • 생산성: SQL을 작성하지 않아도 기본적인 CRUD 작업 가능.
  • 추상화: 데이터베이스 독립성을 유지하며 객체 중심의 프로그래밍 가능.
  • 자동화: 엔티티 상태 관리, 변경 감지, Lazy/Eager Loading 등 자동화된 기능 제공.
  • 캐싱: 1차 캐시와 2차 캐시를 통해 성능 최적화.

단점

  • 학습 곡선: 개념(엔티티 매핑, 연관 관계, 영속성 컨텍스트 등)을 이해하는 데 시간이 필요.
  • 복잡한 쿼리 작성 어려움: JPQL과 Criteria API는 직관적이지 않을 수 있음.
  • 자동화 오버헤드: 자동화된 기능이 성능 이슈를 야기할 가능성 있음(예: N+1 문제).

2. MyBatis

특징

  1. SQL Mapper 기반
    • SQL을 직접 작성하고 XML 또는 어노테이션으로 매핑.
    • SQL의 자유도가 높아 복잡한 쿼리 처리에 유리.
  2. 설정 기반 매핑
    • XML 또는 어노테이션을 통해 Java 객체와 SQL 결과를 매핑.
  3. 직접적인 데이터베이스 제어
    • 개발자가 SQL 작성에 완전한 제어권을 가짐.
    • 복잡한 조인이나 고성능 쿼리를 직접 작성 가능.
  4. 결과 매핑:
    • 쿼리 결과를 POJO(Plain Old Java Object)에 매핑하여 처리.

장점

  • SQL 제어: 복잡한 SQL 작성과 튜닝이 가능.
  • 동적 SQL: 동적 쿼리 작성이 편리.
  • 단순성: SQL Mapper로 구성되어 간단하고 직관적.
  • 효율성: 개발자가 SQL 최적화와 성능 튜닝을 쉽게 제어.

단점

  • 생산성 저하: 모든 SQL을 직접 작성해야 하므로 초기 개발 시간이 오래 걸릴 수 있음.
  • 유지보수 어려움: SQL과 비즈니스 로직이 얽힐 가능성이 높음.
  • 재사용성 제한: SQL 중심으로 설계되어 객체 지향적 설계와는 거리가 멀 수 있음.

3. JPA와 MyBatis 비교

특징 ORM(Object-Relational Mapping) 중심 SQL Mapper 중심
SQL 작성 JPQL 사용 (SQL 추상화) SQL을 직접 작성
개발 속도 자동화된 데이터 관리로 빠른 개발 가능 직접 SQL 작성으로 초기 개발 속도 느림
복잡한 쿼리 복잡한 쿼리는 JPQL로 작성하거나 Native Query 사용 복잡한 쿼리 작성에 적합
데이터베이스 종속성 낮음 (JPQL 및 엔티티 중심) 높음 (SQL에 의존)
성능 최적화 자동화된 매핑과 캐시로 적절한 성능 제공 개발자가 SQL 튜닝으로 성능 최적화 가능
학습 곡선 높음 (ORM과 JPA의 동작 원리 이해 필요) 낮음 (SQL과 매핑 규칙만 알면 됨)
동적 쿼리 처리 어렵거나 불편할 수 있음 (Criteria API 사용) 동적 SQL 작성이 쉬움 (XML <if>, <choose> 지원)
유지 보수 코드에서 비즈니스 로직과 SQL 분리 가능 SQL과 비즈니스 로직이 얽힐 수 있음
캐시 지원 1차 캐시(엔티티 매니저) 및 2차 캐시(Hibernate 등 지원 가능) 직접 구현해야 함 (예: Redis와 연동)
플랫폼 의존성 낮음 (다양한 DB와 쉽게 호환) DB 벤더에 종속될 가능성 있음

 


4. 어떤 상황에서 선택해야 하나?

JPA가 적합한 경우

  • 객체 지향 설계를 선호하고 데이터베이스의 데이터와 객체 간 매핑이 중요한 경우.
  • 데이터 중심보다 도메인 중심의 설계를 원할 때.
  • 표준화된 방식(JPA/ORM)을 통해 프로젝트 확장성과 유지보수성을 고려할 때.
  • DB 독립성을 중시하며 다양한 DB를 지원할 가능성이 있을 때.

MyBatis가 적합한 경우

  • SQL 성능 최적화가 중요한 경우(복잡한 쿼리, 대용량 데이터 처리).
  • 동적 쿼리와 복잡한 JOIN 쿼리를 자주 사용해야 할 때.
  • 데이터베이스 설계가 복잡하고 JPA 매핑이 어렵거나 비효율적인 경우.
  • 레거시 시스템과 통합이 필요한 경우.

5. 결론

  • JPA는 생산성을 높이고 유지보수를 쉽게 하기 위한 ORM 기반 도구로 적합하며 비즈니스 로직이 복잡하거나 객체 지향 설계가 중요한 프로젝트에서 강점.
  • MyBatis는 SQL 작성의 자유도와 데이터베이스 성능 최적화가 중요한 프로젝트에서 강점.

두 프레임워크는 상황에 따라 병행해서 사용하는 것도 가능합니다. 예를 들어, JPA를 기본으로 사용하되 복잡한 쿼리가 필요한 부분은 MyBatis로 처리하는 식으로 하이브리드 접근을 고려할 수도 있습니다.

반응형

'Spring Framework' 카테고리의 다른 글

[Spring] Lombok  (1) 2024.12.18
[Spring] Dispatcher Servlet  (2) 2024.11.22
[Spring] Maven vs Gradle  (3) 2024.11.19
[Spring] 정적 팩토리 메서드 (Static Factory Method)  (2) 2024.11.14

 

Spring 공부, 개발 중 Dispatcher-Servlet이라는 용어를 자주 접해 궁금증이 생겨 학습해볼 기회를 가지게 되었습니다.

Dispatcher Servlet 란?

Spring MVC의 핵심 구성요소로 클라이언트 요청을 처리하고 적절한 핸들러로 전달하는 프론트 컨트롤러

주요 역할

  1. 요청 분배 : HTTP 요청을 받아 적합한 Controller에 전달
  2. 결과 처리 : Controller의 응답(View 또는 데이터)을 클라이언트에 반환
  3. 확장성 : HandlerMapping, ViewResolver 등 다양한 컴포넌트를 조합해 요청 처리를 유연하게 구성 가능.

출처)

 

DispatcherServlet :: Spring Framework

Spring MVC, as many other web frameworks, is designed around the front controller pattern where a central Servlet, the DispatcherServlet, provides a shared algorithm for request processing, while actual work is performed by configurable delegate components

docs.spring.io

처리 과정

  1. Handler Mapping
    • 요청 URL을 기반으로 적합한 핸들러(Controller)를 탐색
    • 여러 HandlerMapping 구현체 중 적합한 매핑 정보를 제공
  2. Handler Adapter
    • 선택된 핸들러를 실행할 수 있도록 어댑터 역할
    • Controller 메서드를 호출하고 결과 반환
  3. View Resolver
    • Controller가 반환한 논리적인 View 이름을 물리적인 View 객체로 변환
    • 변환된 View를 사용해 응답을 생성

정리

  • 일종의 요청을 핸들러로 매핑해주는 역할 수행
반응형

'Spring Framework' 카테고리의 다른 글

[Spring] Lombok  (1) 2024.12.18
[Spring] JPA vs MyBatis  (0) 2024.12.03
[Spring] Maven vs Gradle  (3) 2024.11.19
[Spring] 정적 팩토리 메서드 (Static Factory Method)  (2) 2024.11.14

필요한 라이브러리를 불러오고 빌드하는 life Cycle을 관리해주는 툴

레거시한 프로젝트는 Maven이지만, 최근 Gradle로 넘어오는 추세

빌드

: 소스코드 파일을 컴퓨터에서 실행할 수 있는 독립적인 형태로 변환하는 과정과 결과

  • spring boot
    • 작성한 소스코드(.java) + 프로젝트에 사용한 파일 및 자원(.xml, .properties, .jpa, .jpg)을 jvm이나 톰캣 같은 was가 인식할 수 있도록 패키징하는 과정 및 결과물
    • 빌드를 하면, 소스코드를 컴파일 해서 .class로 변환하고, resource를 .class가 참조할 수 있는 적절한 위치로 옮기고 META-INF와 MANIFEST.MF들을 하나로 압축하는 과정을 의미

빌드 관리 도구

: 소스코드에서 애플리케이션을 생성하면서 사용하는 필요한 라이브러리를 자동으로 관리해줌.

빌드 관리 도구가 수행하는 작업

  • 종속성 다운로드 - 전처리(preprocessing)
  • 소스코드를 바이너리 코드로 컴파일(Compile)
  • 바이너리 코드를 패키징(Packaging)
  • 테스트 실행(Testing)
  • 프로덕션 시스템에 배포(distribution)

Maven

정의

: Java전용 프로젝트 관리 도구로, Lifecycle 관리 목적 빌드 도구이며, Apache Ant의 대안으로 만들어짐. Apache라이센스로 배포되는 오픈 소스 소프트웨어.

특징

  • Lifecycle 관리 도구로, 정해진 Lifecycle에 의해 작업을 수행하며, 전반적인 프로젝트 관리 기능을 포함
  • 필요한 라이브러리를 pom.xml에 정의 ⇒ 프로젝트 모델링
    • pom : Project Object Model
      • 프로젝트 정보(프로젝트 이름, 라이센스, …)
      • 빌드 설정(소스, 리소스, Lifecycle별 실행한 plugin 등 빌드 관련 설정)
      • 빌드 환경( 사용자 환경 별로 달라질 수 있는 프로필 정보)
      • pom 연관 정보(의존 프로젝트, 모듈, 상위 프로젝트 등)
    • pom.xml을 이용한 정형화된 빌드 시스템으로 다양한 라이브러리를 관리하며 네트워크를 통해 자동으로 다운을 받아줌.
  • 기존 Apache Ant (Java로 개발된 어플리케이션으로 커맨드 라인 형태의 빌드, 배포를 위한 도구)라는 빌드 도구를 많이 사용했지만 Maven이 Ant를 넘어서 개발자들이 많이 사용)

Lifecycle 순서

  1. clean : 빌드 시 생성되어있었던 파일들을 삭제
  2. validate : 프로젝트가 올바른지 확인하고 필요한 모든 정보를 사용할 수 있는지 확인하는 단계
  3. compile : 프로젝트 소스코드를 컴파일 하는 단계
  4. test : 단위 테스트를 수행하는 단계. 테스트 실패 시 빌드 실패로 처리하며, 스킵이 가능하다.
  5. package : 실제 컴파일된 소스 코드와 리소스들을 jar, war등의 파일을 배포를 위한 패키지로 만든다.
  6. verify : 통합 테스트 결과에 대한 검사를 실행하여 품질 기준을 충족하는지 확인한다.
  7. site : 프로젝트 문서와 사이트 작성, 생성하는 단계
  8. deploy : 만들어진 package를 원격 저장소에 release하는 단계

Apache Ant vs Maven

Apache Ant는 비교적 자유도가 높은 편이고, Maven은 정해진 Lifecycle에 의해 작업을 수행하며, 전반적인 프로젝트 관리 기능까지 포함하는 차이점이 있다.


Gradle

정의

: Maven을 대체할 수 있는 프로젝트 구성 관리 및 범용 빌드 툴

  • Ant Builder와 Groovy script를 기반으로 구축되어 기존 Ant의 역할과 배포스크립트의 기능을 모두 사용가능
  • 스프링 부트와 안드로이드에서도 사용
  • 빌드 속도가 Maven에 비해 10~100배 가량 빠르다
  • Java, C/C++, Python 등을 지원

Groovy

: JVM에서 실행되는 스크립트 언어

  • JVM에서 동작하지만 소스코드를 컴파일할 필요 없다.
  • Java와 호환되며, Java class file들을 Groovy class로 사용 가능하다.
  • Java 문법과 유사하여 빌드 처리를 관리할 수 있다.

특징

  • 가독성이 좋다 : 코딩에 의한 간결한 정의가 가능
  • 재사용에 용이 : 설정 주입 방식(Configuration Injection)을 사용하므로 재사용에 용이
  • 구조적인 장점 : Build Script를 Groovy기반 DSL(Domail Specific Language)를 사용해 코드로서 설정 정보를 구성하므로 구조적인 장점이 있다.
  • 편리함 : Gradle 설치 없이 Gradle wrapper를 이용해 빌드 지원
  • 멀티 프로젝트 : Gradle은 멀티 프로젝트 빌드를 지원하기 위해 설계된 빌드 관리 도구
  • 지원 : Maven을 완전 지원

Maven vs Gradle

  1. 스크립트 길이와 가독성 측면에서 Gradle이 우세
  2. 빌드와 테스트 실행 결과 Gradle이 더 빠름
    1. gradle은 캐시를 사용하므로 테스트 반복 시 실행 결과 시간의 차이가 더 커진다.
  3. 의존성이 늘어날 수록 스크립트 품질의 차이가 커진다.
    1. Maven은 멀티 프로젝트에서 특정 설정을 다른 모듈에서 사용하려면 상속을 받아야 한다.
    2. Gradle은 설정 주입 방식을 사용해 멀티 프로젝트에 적합하다.
반응형

'Spring Framework' 카테고리의 다른 글

[Spring] Lombok  (1) 2024.12.18
[Spring] JPA vs MyBatis  (0) 2024.12.03
[Spring] Dispatcher Servlet  (2) 2024.11.22
[Spring] 정적 팩토리 메서드 (Static Factory Method)  (2) 2024.11.14

 

흑백배달기사 프로젝트 진행 중 PR을 통해 팀원과 코드리뷰를 남기는 상황에서 
"정적팩토리는 무엇이고, 장점이 뭘까?"
궁금증이 생겼습니다.


 

정적 팩토리 메서드 ?

정적(Static), 팩토리(Factory), 메서드(Method)

  • 정적
  • 팩토리
    • GoF 디자인 패턴 중 팩토리 패턴에서 유래
    • 객체를 생성하는 역할을 분리
  • 메서드
정적 팩토리 메서드 => "객체 생성의 역할을 하는 클래스 메서드"

예시

// LocalTime.class
...
public static LocalTime of(int hour, int minute) {
  ChronoField.HOUR_OF_DAY.checkValidValue((long)hour);
  if (minute == 0) {
    return HOURS[hour];
  } else {
    ChronoField.MINUTE_OF_HOUR.checkValidValue((long)minute);
    return new LocalTime(hour, minute, 0, 0);
  }
}
...

// hour, minutes을 인자로 받아서 9시 30분을 의미하는 LocalTime 객체를 반환한다.
LocalTime openTime = LocalTime.of(9, 30);
  • 예제에 따르면
    • LocalTime 클래스의 생성자가 아닌 of메서드로 객체를 생성하고 있다.
public enum Color {
  RED,
  BLUE;
}
...
Color redColor = Color.valueOf("RED");
Color blueColor = Color.valueOf("BLUE");
  • enum의 요소를 조회할 때 사용하는 valueOf도 정적팩토리메서드의 일종이다.

왜 필요한가?

생성자가 있는데, 왜 굳이 정적 팩토리 메서드를 따로 만들어서 객체를 생성할까?

생성자와 차이

생성자 대신 정적 팩토리 메서드를 고려하라 by. 조슈아 블로크의 "이펙티브 자바"에서..

1. 이름을 가질 수 있다.

  • 객체는 생성 목적과정에 따라 생성자를 구별할 필요가 있다.
  • new 키워드를 사용한다면,
    • 객체를 생성하는 생성자는 내부 구조를 잘 알고 있어야 목적에 맞게 객체를 생성할 수 있다.
  • 정적 팩토리 메서드를 사용한다면,
    • 메서드 이름에 객체의 생성 목적을 담을 수 있다.
  • 예시) 자동로또와 수동로또를 생성하는 코드
    • createAutoLotto (자동로또)와 createManualLotto (수동로또) 모두 로또 객체를 생성하고 반환하는 정적 팩토리 메서드이다.
      • 이름만 보고도 객체의 역할을 이해할 수 있다.
      • 즉, 생성 목적을 표현함으로써 가독성이 좋아진다.
public class LottoFactory() {
  private static final int LOTTO_SIZE = 6;

  private static List<LottoNumber> allLottoNumbers = ...; // 1~45까지의 로또 넘버

  public static Lotto createAutoLotto() { // 자동로또
    Collections.shuffle(allLottoNumbers);
    return new Lotto(allLottoNumbers.stream()
            .limit(LOTTO_SIZE)
            .collect(Collectors.toList()));
  }

  public static Lotto createManualLotto(List<LottoNumber> lottoNumbers) { // 수동로또
    return new Lotto(lottoNumbers);
  }
  ...
}

 

2. 호출할 때마다 새로운 객체를 생성할 필요가 없다.

  • enum과 같이 자주 사용되는 요소의 개수가 정해져있다면,
    그 수만큼 미리 생성해놓고 조회(캐싱)할 수 있는 구조로 만들 수 있다.
    • " 정적팩토리메서드 + 캐싱구조 "를 함께 사용하면 매번 새로운 객체를 생성할 필요가 없어진다.
  • 예시
    • 다음 코드는 로또 번호를 생성하는 메서드이다.
    • 여기서 중요한 점
      • 미리 생성된 로또 번호 객체의 캐싱을 통해 새로운 객체 생성의 부담을 덜 수 있다
      • 생성자 접근 제한자를 private으로 설정함으로써 객체 생성을 정적팩토리메서드로만 가능하도록 제한할 수 있다.
        • 정해진 범위를 벗어나는 로또 번호의 생성을 막을 수 있다는 장점을 확보할 수 있다.
public class LottoNumber {
  private static final int MIN_LOTTO_NUMBER = 1;
  private static final int MAX_LOTTO_NUMBER = 45;

  private static Map<Integer, LottoNumber> lottoNumberCache = new HashMap<>();

  static {
    IntStream.range(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER)
                .forEach(i -> lottoNumberCache.put(i, new LottoNumber(i)));
  }

  private int number;

  private LottoNumber(int number) {
    this.number = number;
  }

  public LottoNumber of(int number) {  // LottoNumber를 반환하는 정적 팩토리 메서드
    return lottoNumberCache.get(number);
  }

  ...
}

3. 하위 자료형 객체를 반환할 수 있다.

  • 하위 자료형 객체를 반환하는 정적 팩토리 메서드의 특징은 상속을 사용할 때 확인할 수 있다.
    • 생성자 역할을 하는 정적팩토리메서드가 반환값을 가지고 있기 때문
  • 예시
    • Basic, Intermediate, Advanced클래스가 Level이라는 상위 타입을 상속받는 구조라고 가정할때,
      시험 점수에 따라 결정되는 하위 등급 타입을 반환하는 정적팩토리메서드를 만들면, 다음과 같이 분기처리를 통해 하위 타입의 객체를 반환할 수 있다.
public class Level {
  ...
  public static Level of(int score) {
    if (score < 50) {
      return new Basic();
    } else if (score < 80) {
      return new Intermediate();
    } else {
      return new Advanced();
    }
  }
  ...
}

4. 객체 생성을 캡슐화할 수 있다.

캡슐화
데이터의 은닉을 의미.
여기서는 생성자를 클래스의 메서드로 숨기면서 내부 상태를 외부에 드러낼 필요없이 객체 생성 인터페이스를 단순화 시킬 수 있다.
  • 개발을 하다보면 계층 간 데이터를 전송하기 위해 DTO (Data Transfer Object)를 정의해서 사용한다.
    • 이를 위해선, DTO <=> Entity 간 형 변환이 자유롭게 가능해야 한다.
    • 정적팩토리메서드를 사용한다면,
      • 내부 구현을 모르더라도 쉽게 변환할 수 있다.
  • 정적팩토리메서드 예시
public class CarDto {
  private String name;
  private int position;

  pulbic static CarDto from(Car car) {
    return new CarDto(car.getName(), car.getPosition());
  }
}


// Car -> CatDto 로 변환
CarDto carDto = CarDto.from(car);
  • DTO 예시
    • 외부에서 생성자의 내부 구현을 모두 드러내야 한다.
Car carDto = CarDto.from(car); // 정적 팩토리 메서드를 쓴 경우
CarDto carDto = new CarDto(car.getName(), car.getPosition); // 생성자를 쓴 경우

정리

  • 정적팩토리메서드는 생성자의 역할을 대신한다.
  • 객체 생성의 목적과 과정을 네이밍에 반영해, 가독성 좋은 코드를 작성하고 OOP에 적합하다.
  • 도메인에서 "객체 생성"의 역할 자체가 중요하다면, 정적팩토리클래스를 따로 분리해도 좋다.
    • 단, 팩토리 메서드만 존재하는 클래스를 생성할 경우 상속이 불가능 하다.
  • 객체간 형 변환, 객체 생성이 빈번한 경우 사용에 용이

정적 팩토리 메서드 네이밍 컨벤션

  • from : 하나의 매개 변수를 받아서 객체를 생성
  • of : 여러개의 매개 변수를 받아서 객체를 생성
  • getInstance / instance : 인스턴스를 생성, 이전에 반환했던 것과 같을 수 있음
  • newInstance / create : 새로운 인스턴스를 생성
  • get[OtherType] : 다른 타입의 인스턴스를 생성, 이전에 반환했던 것과 같을 수 있음
  • new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.

출처)

 

정적 팩토리 메서드(Static Factory Method)는 왜 사용할까?

tecoble.techcourse.co.kr

 

 

정적 팩토리 메서드 (Static Factory Method)

간단하게 DTO, Entity에서 사용할 정적 팩토리 메서드 정리

velog.io

 

반응형

'Spring Framework' 카테고리의 다른 글

[Spring] Lombok  (1) 2024.12.18
[Spring] JPA vs MyBatis  (0) 2024.12.03
[Spring] Dispatcher Servlet  (2) 2024.11.22
[Spring] Maven vs Gradle  (3) 2024.11.19

+ Recent posts