Static Factory Method (정적 팩토리 메서드)
Java를 공부하다 보면 다음과 같은 코드를 자주 보게 된다.
LocalDate.now();
Optional.of(value);
List.of("A", "B", "C");
ResponseEntity.ok(data);
LoggerFactory.getLogger(UserService.class);
처음에는 단순히 편의 메서드라고 생각하기 쉽지만, 사실 이들은 모두 Static Factory Method(정적 팩토리 메서드) 를 활용한 대표적인 예시다.
실무에서는 생성자보다 정적 팩토리 메서드를 사용하는 경우가 매우 많다.
1. Static Factory Method란?
객체를 생성할 때 생성자를 직접 호출하지 않고, static 메서드를 통해 객체를 생성하는 방법이다.
일반적인 객체 생성 방식
User user = new User("kim", "1234");
정적 팩토리 메서드 방식
User user = User.create("kim", "1234");
둘 다 객체를 생성하지만 생성 책임이 생성자가 아닌 메서드에 있다.
2. 왜 사용하는가?
생성자는 몇 가지 한계가 있다.
public User(String id, String password) {
...
}
코드만 보고는 이 생성자가 어떤 의미인지 알기 어렵다.
User user = new User("kim", "1234");
반면 정적 팩토리 메서드는 이름을 가질 수 있다.
User user = User.createUser("kim", "1234");
의도가 훨씬 명확해진다.
3. 가장 기본적인 예제
생성자 방식
public class User {
private String id;
public User(String id) {
this.id = id;
}
}
사용
User user = new User("kim");
정적 팩토리 메서드 방식
public class User {
private String id;
private User(String id) {
this.id = id;
}
public static User create(String id) {
return new User(id);
}
}
사용
User user = User.create("kim");
생성자를 private으로 막고 생성 경로를 통제할 수 있다.
4. 이름을 가질 수 있다
생성자는 이름이 클래스명으로 고정된다.
new User(...)
반면 정적 팩토리 메서드는 의미 있는 이름을 붙일 수 있다.
public static User createAdmin(String id) {
return new User(id, Role.ADMIN);
}
public static User createUser(String id) {
return new User(id, Role.USER);
}
사용
User admin = User.createAdmin("kim");
User user = User.createUser("lee");
코드만 봐도 역할이 명확하다.
5. 생성자 오버로딩 문제 해결
생성자만 사용하면 이런 일이 발생한다.
public User(String id) {}
public User(String id, String password) {}
public User(String id, String password, Role role) {}
매개변수가 늘어나면 의미를 파악하기 어렵다.
new User("kim", "1234", Role.ADMIN);
정적 팩토리 메서드를 사용하면
User.createAdmin("kim", "1234");
User.createNormalUser("kim", "1234");
가독성이 좋아진다.
6. 캐싱 가능
생성자는 호출할 때마다 새 객체를 만든다.
new Integer(100);
new Integer(100);
서로 다른 객체다.
정적 팩토리 메서드는 이미 존재하는 객체를 반환할 수 있다.
public static User getGuestUser() {
return GUEST_USER;
}
User user1 = User.getGuestUser();
User user2 = User.getGuestUser();
동일 객체를 반환할 수 있다.
7. 반환 타입을 숨길 수 있다
인터페이스를 반환할 수도 있다.
public static List<String> createList() {
return new ArrayList<>();
}
public static List<String> createList() {
return new LinkedList<>();
}
public static List<String> createList() {
return List.of();
}
사용자는
List<String> list = User.createList();
만 알 뿐 실제 구현체가
ArrayList
LinkedList
ImmutableList
중 무엇인지는 몰라도 된다.
new ArrayList();를 한다면 구현체를 바꾸면 호출하는 쪽도 바꿔야 하지만
인터페이스를 리턴하면 구현체만 바꾸고 호출하는 소스는 바꾸지 않아도 된다.
ArrayList
LinkedList
ImmutableList
CustomList
뭐로 바꾸든 사용자는 추상화에만 의존. (구현을 숨긴다)
8. 실제 JDK 예제
Optional
Optional.of(value);
Optional.ofNullable(value);
Optional.empty();
생성자를 직접 사용하지 않는다.
List
List.of("A", "B", "C");
Java 9부터 추가된 대표적인 정적 팩토리 메서드다.
Map
Map.of(
"A", 1,
"B", 2
);
LocalDate
LocalDate.now();
LocalDate.of(2025, 1, 1);
9. Spring에서 자주 보는 예제
ResponseEntity
return ResponseEntity.ok(data);
실제로는
new ResponseEntity<>(
data,
HttpStatus.OK
);
를 감춘 것이다.
LoggerFactory
private static final Logger log =
LoggerFactory.getLogger(UserService.class);
Spring 프로젝트에서 거의 필수적으로 사용된다.
10. Effective Java가 권장하는 이유
Effective Java 의 Item 1은
Consider static factory methods instead of constructors
(생성자 대신 정적 팩토리 메서드를 고려하라)
를 첫 번째 주제로 다룬다.
그만큼 Java 설계에서 중요한 개념이다.
장점
1. 이름을 가질 수 있다
User.createAdmin();
2. 객체 생성 통제 가능
Singleton.getInstance();
3. 캐싱 가능
Integer.valueOf();
//캐싱하거나
public static Integer valueOf(int i) {
// 캐시에 있으면
return cachedObject;
// 없으면 새로 생성
return new Integer(i);
}
//항상 같은 객체를 반환하거나 자유
public static User getGuestUser() {
return GUEST_USER;
}
Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a == b); // false
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true
Integer a = Integer.valueOf(1000);
Integer b = Integer.valueOf(1000);
System.out.println(a == b); // false
개념적으로 내부 구조는 아래와 같음
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127) {
return CACHE[i + 128];
}
return new Integer(i);
}
Static Factory Method는 반드시 새로운 객체를 만들 필요가 없다. 이미 생성된 객체를 캐시하여 반환할 수 있다. 대표적인 예가 Integer.valueOf()이며, -128~127 범위의 Integer 객체를 캐시하여 동일한 객체를 재사용함으로써 메모리 사용량과 객체 생성 비용을 줄인다.
4. 반환 타입을 숨길 수 있다
List.of();
5. 인터페이스 반환 가능
List<String> list = ...
단점
1. 상속이 어려움
생성자를 private으로 막으면 상속 불가능
private User() {}
2. 생성자보다 찾기 어려움
User.create()
User.of()
User.from()
User.valueOf()
어떤 메서드가 있는지 IDE 도움을 받아야 한다.
실무에서 자주 사용하는 네이밍 규칙
| of() | 여러 값을 받아 생성 |
| from() | 다른 객체를 변환 |
| valueOf() | 값을 객체로 변환 |
| getInstance() | 기존 객체 반환 가능 |
| newInstance() | 항상 새 객체 생성 |
| create() | 일반 생성 |
| ofNullable() | null 허용 생성 |
예시
LocalDate.of();
Optional.of();
Optional.ofNullable();
Integer.valueOf();
LoggerFactory.getLogger();
면접 한 줄 정리
Static Factory Method는 생성자를 직접 호출하지 않고 static 메서드를 통해 객체를 생성하는 방식으로, 의미 있는 이름 부여, 생성 로직 캡슐화, 객체 재사용, 반환 타입 은닉 등의 장점을 제공한다.
실무에서는 생성자보다
List.of()
Optional.of()
LocalDate.now()
ResponseEntity.ok()
LoggerFactory.getLogger()
와 같은 정적 팩토리 메서드를 훨씬 자주 접하게 된다.
'language > java' 카테고리의 다른 글
| List.of에 대하여 (0) | 2026.06.01 |
|---|---|
| Factory Method Pattern (팩토리 메서드 패턴) (0) | 2026.06.01 |
| Virtual Thread (Java 21) 완벽 이해하기 (0) | 2026.06.01 |
| 병렬 Stream(parallelStream) 주의점 완벽 이해하기 (0) | 2026.06.01 |
| ConcurrentHashMap 동시성 처리 완벽 이해하기 (0) | 2026.06.01 |
댓글