### 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 주입에 사용됩니다. - **예시**: ```java 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` - **설명**: 동일한 인터페이스를 구현한 여러 클래스가 있을 때, 특정 구현체를 선택할 때 사용됩니다. - **예시**: ```java 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 인젝터가 단일 인스턴스를 재사용합니다. - **예시**: ```java 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` - **설명**: 모듈 내에서 복잡한 객체 생성 로직을 정의할 때 사용됩니다. - **예시**: ```java 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` - **설명**: 인터페이스의 기본 구현체를 지정하여 모듈 설정 없이도 의존성을 주입할 수 있습니다. - **예시**: ```java 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 클래스를 통해 동적으로 의존성을 제공합니다. - **예시**: ```java 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 { @Override public Service get() { return new DynamicService(); } } class App { @Inject private Service service; public void run() { service.execute(); // 출력: 동적 서비스 실행 } } ``` - **특징**: Provider를 통해 런타임에 객체를 생성할 수 있어 동적 처리가 가능합니다. --- #### 7. `@Qualifier` - **설명**: 사용자 정의 어노테이션을 만들어 더 세밀한 의존성 구분을 가능하게 합니다. - **예시**: ```java 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의 어노테이션들은 의존성 주입을 간단하고 유연하게 만들어줍니다. 위 예시를 통해 각 어노테이션의 사용법과 장점을 이해할 수 있으며, 프로젝트 요구사항에 맞게 적절히 선택해 사용하면 됩니다. 추가 질문이 있다면 언제든 물어보세요!