8.9 KiB
8.9 KiB
Guice의 주요 어노테이션과 사용 예시
Google Guice는 의존성 주입(Dependency Injection, DI)을 간편하게 구현할 수 있는 경량 프레임워크로, 다양한 어노테이션을 통해 의존성을 정의하고 관리합니다. 아래에서는 Guice에서 자주 사용되는 주요 어노테이션들을 표로 정리하고, 각 어노테이션의 역할과 사용 예시를 설명하겠습니다.
Guice 주요 어노테이션 표
| 어노테이션 | 설명 | 사용 위치 | 주요 특징 및 용도 |
|---|---|---|---|
@Inject |
의존성을 주입할 위치를 지정합니다. 생성자, 필드, 메서드에 사용 가능합니다. | 생성자, 필드, Setter 메서드 | Guice가 자동으로 의존성을 주입하도록 지시. 선택적 주입 가능 (@Inject(optional=true)). |
@Named |
동일한 타입의 여러 구현체 중 특정 구현체를 선택하기 위해 사용됩니다. | 필드, 매개변수 | 문자열 키로 구체적인 바인딩을 식별. |
@Singleton |
클래스의 인스턴스가 단일 객체로 유지되도록 지정합니다. | 클래스 | 싱글턴 패턴을 구현하여 메모리 효율성 향상. |
@Provides |
모듈 내에서 의존성을 제공하는 메서드를 정의합니다. | 모듈의 메서드 | 복잡한 객체 생성 로직을 직접 작성 가능. |
@ImplementedBy |
인터페이스의 기본 구현체를 지정합니다. | 인터페이스 | 모듈 없이 기본 바인딩을 설정. |
@ProvidedBy |
동적으로 의존성을 제공하는 클래스를 지정합니다. | 인터페이스 | Provider 클래스를 통해 의존성 공급. |
@Qualifier |
사용자 정의 qualifier를 생성하기 위한 메타 어노테이션입니다. | 사용자 정의 어노테이션 | @Named보다 더 세밀한 의존성 구분 가능. |
어노테이션별 설명 및 예시
1. @Inject
- 설명: Guice가 의존성을 주입할 위치를 나타냅니다. 생성자 주입, 필드 주입, Setter 주입에 사용됩니다.
- 예시:
import com.google.inject.Inject;
class UserService {
private final Database database;
@Inject
public UserService(Database database) { // 생성자 주입
this.database = database;
}
@Inject
private Logger logger; // 필드 주입
@Inject
public void setConfig(Config config) { // Setter 주입
// 설정 로직
}
}
- 특징: 생성자 주입이 가장 권장되며, 불변성을 보장합니다. 필드 주입은 간단하지만 테스트 시 불편할 수 있습니다.
2. @Named
- 설명: 동일한 인터페이스를 구현한 여러 클래스가 있을 때, 특정 구현체를 선택할 때 사용됩니다.
- 예시:
import com.google.inject.Inject;
import com.google.inject.name.Named;
interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() { System.out.println("MySQL 연결"); }
}
class PostgresDatabase implements Database {
public void connect() { System.out.println("Postgres 연결"); }
}
class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Database.class).annotatedWith(Names.named("mysql")).to(MySQLDatabase.class);
bind(Database.class).annotatedWith(Names.named("postgres")).to(PostgresDatabase.class);
}
}
class App {
@Inject @Named("mysql")
private Database database;
public void run() {
database.connect(); // 출력: MySQL 연결
}
}
- 특징: 문자열 기반으로 동작하며, 오타에 주의해야 합니다.
3. @Singleton
- 설명: 클래스의 인스턴스를 싱글턴으로 관리하도록 지정합니다. Guice 인젝터가 단일 인스턴스를 재사용합니다.
- 예시:
import com.google.inject.Singleton;
import com.google.inject.Inject;
@Singleton
class Cache {
public void store(String key, String value) {
System.out.println("캐시에 저장: " + key + " -> " + value);
}
}
class App {
@Inject
private Cache cache;
public void run() {
cache.store("user", "data"); // 동일한 인스턴스 사용
}
}
- 특징: 메모리 효율성을 높이지만, 상태를 공유하므로 주의가 필요합니다.
4. @Provides
- 설명: 모듈 내에서 복잡한 객체 생성 로직을 정의할 때 사용됩니다.
- 예시:
import com.google.inject.Provides;
import com.google.inject.AbstractModule;
interface Client {
void send(String msg);
}
class HttpClient implements Client {
private final String url;
public HttpClient(String url) { this.url = url; }
public void send(String msg) { System.out.println("Sending to " + url + ": " + msg); }
}
class MyModule extends AbstractModule {
@Provides
public Client provideClient() {
return new HttpClient("https://api.example.com");
}
}
class App {
@Inject
private Client client;
public void run() {
client.send("Hello"); // 출력: Sending to https://api.example.com: Hello
}
}
- 특징: 동적 생성 로직을 커스터마이징할 수 있어 유연성이 높습니다.
5. @ImplementedBy
- 설명: 인터페이스의 기본 구현체를 지정하여 모듈 설정 없이도 의존성을 주입할 수 있습니다.
- 예시:
import com.google.inject.ImplementedBy;
import com.google.inject.Inject;
@ImplementedBy(DefaultService.class)
interface Service {
void execute();
}
class DefaultService implements Service {
public void execute() { System.out.println("기본 서비스 실행"); }
}
class App {
@Inject
private Service service;
public void run() {
service.execute(); // 출력: 기본 서비스 실행
}
}
- 특징: 모듈 설정이 필요 없어 간단하지만, 유연성이 제한적입니다.
6. @ProvidedBy
- 설명: Provider 클래스를 통해 동적으로 의존성을 제공합니다.
- 예시:
import com.google.inject.ProvidedBy;
import com.google.inject.Provider;
import com.google.inject.Inject;
@ProvidedBy(DynamicServiceProvider.class)
interface Service {
void execute();
}
class DynamicService implements Service {
public void execute() { System.out.println("동적 서비스 실행"); }
}
class DynamicServiceProvider implements Provider<Service> {
@Override
public Service get() {
return new DynamicService();
}
}
class App {
@Inject
private Service service;
public void run() {
service.execute(); // 출력: 동적 서비스 실행
}
}
- 특징: Provider를 통해 런타임에 객체를 생성할 수 있어 동적 처리가 가능합니다.
7. @Qualifier
- 설명: 사용자 정의 어노테이션을 만들어 더 세밀한 의존성 구분을 가능하게 합니다.
- 예시:
import com.google.inject.Qualifier;
import com.google.inject.Inject;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@interface PrimaryDB {}
class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Database.class).annotatedWith(PrimaryDB.class).to(MySQLDatabase.class);
}
}
class App {
@Inject @PrimaryDB
private Database database;
public void run() {
database.connect(); // 출력: MySQL 연결
}
}
- 특징:
@Named보다 타입 안전성이 높고, 오타 문제를 방지할 수 있습니다.
Guice 어노테이션 사용 팁
- 생성자 주입 우선:
@Inject를 생성자에 사용해 불변성과 명확성을 유지하세요. - Qualifier 활용:
@Named대신 사용자 정의@Qualifier를 사용하면 더 안전합니다. - 싱글턴 주의:
@Singleton은 상태를 공유하므로 스레드 안전성을 고려해야 합니다. - 모듈 분리: 복잡한 프로젝트에서는
@Provides를 활용해 모듈을 깔끔하게 관리하세요.
결론
Guice의 어노테이션들은 의존성 주입을 간단하고 유연하게 만들어줍니다. 위 예시를 통해 각 어노테이션의 사용법과 장점을 이해할 수 있으며, 프로젝트 요구사항에 맞게 적절히 선택해 사용하면 됩니다. 추가 질문이 있다면 언제든 물어보세요!