Blue Chicken - The Legend Of Zelda
본문 바로가기

개인공부

[java]제네릭 타입, 여러가지 객체를 담을 수 있는 타입으로 공통적으로 쓰일 response 만들기

제네릭 타입, 여러가지 객체를 담을 수 있는 타입으로 공통적으로 쓰일 response 만들기

 

참고 : https://st-lab.tistory.com/153

 

제네릭(Generic)은 Java에서 여러 가지 타입을 지원하기 위해 만들어진 기능입니다. 제네릭을 사용하면 클래스나 메소드를 다양한 데이터 타입으로 처리할 수 있습니다. 제네릭은 코드의 재사용성을 높이고, 타입 안전성을 확보할 수 있는 장점이 있습니다.

### 쉽게 설명하기

생각해봅시다: 당신이 여러 종류의 물건을 담을 수 있는 **박스**를 가지고 있다고 상상해보세요. 이 박스에는 사과, 책, 장난감 등 다양한 물건을 넣을 수 있습니다. 이 박스를 코드로 표현해봅시다.

**1. 제네릭이 없는 상황 (구체적인 타입)**

박스를 만들 때, 사과만 담을 수 있도록 만들었다고 가정해봅시다.

```java

class AppleBox {
    private Apple apple;

    public void put(Apple apple) {
        this.apple = apple;
    }

    public Apple get() {
        return apple;
    }
}


```

여기서는 `AppleBox`라는 박스가 사과만 담을 수 있습니다. 만약 책을 담고 싶다면 새로운 `BookBox`를 만들어야 합니다.

```java

class BookBox {
    private Book book;

    public void put(Book book) {
        this.book = book;
    }

    public Book get() {
        return book;
    }
}


```

**2. 제네릭을 사용한 상황 (다양한 타입)**

이제 제네릭을 사용해서 다양한 물건을 담을 수 있는 **박스**를 만들어 봅시다.

```java

class Box<T> {
    private T item;

    public void put(T item) {
        this.item = item;
    }

    public T get() {
        return item;
    }
}


```

여기서 `T`는 제네릭 타입으로, 어떤 타입이든 될 수 있습니다. 이제 이 `Box` 클래스를 사용해서 다양한 타입의 박스를 만들 수 있습니다.

```java

Box<Apple> appleBox = new Box<>(); // 사과를 담는 박스
appleBox.put(new Apple());

Box<Book> bookBox = new Box<>(); // 책을 담는 박스
bookBox.put(new Book());


```

이제 `Box` 클래스를 만들어두면 사과, 책, 장난감 등 어떤 물건이든 담을 수 있습니다. 이렇게 하면 박스를 여러 종류로 만들 필요 없이 **하나의 박스**로 모든 물건을 처리할 수 있게 됩니다.

### 제네릭의 장점

1. **코드 재사용**: 동일한 코드를 다양한 데이터 타입에 대해 재사용할 수 있습니다. 여러 타입을 처리하기 위해 새로운 클래스를 만들 필요가 없습니다.

2. **타입 안전성**: 제네릭을 사용하면 컴파일 시점에 타입을 체크할 수 있습니다. 잘못된 타입이 사용되면 컴파일 에러가 발생하므로, 런타임 오류를 줄일 수 있습니다.

3. **유연성**: 제네릭 클래스를 사용하면 코드가 더 유연해집니다. 하나의 클래스로 다양한 타입을 처리할 수 있어, 코드가 더 간결해집니다.

### 예시: 제네릭을 사용한 `Response`

```java

import lombok.Data;

@Data
import java.util.List;

public class Response<T> {
    private boolean success;
    private String message;
    private T data;

    public Response(boolean success, String message, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    // Getter와 Setter 메소드
}


```

이 `Response` 클래스는 `T`라는 제네릭 타입을 사용합니다. `T`는 사용자가 지정하는 타입으로, `User`, `String`, `Integer` 등 어떤 타입이든 될 수 있습니다.

- `Response<User>`: 로그인에 성공했을 때, 사용자 정보를 반환할 수 있습니다.
- `Response<String>`: 로그인에 실패했을 때, 오류 메시지 같은 간단한 데이터를 반환할 수 있습니다.

이렇게 제네릭을 사용하면 코드가 훨씬 재사용 가능하고, 다양한 상황에 적응할 수 있게 됩니다.

 

제네릭 타입에서 리스트 받기


제네릭 타입은 `List<T>`와 같은 컬렉션 타입도 사용할 수 있습니다. 즉, 제네릭 클래스에서 리스트를 받을 수 있습니다.

다음은 `Response` 클래스에서 리스트를 제네릭 타입으로 받는 예제입니다.

```java

import java.util.List;

public class Response<T> {
    private boolean success;
    private String message;
    private T data;

    public Response(boolean success, String message, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    // Getter와 Setter 메소드
}


```

이 `Response` 클래스는 단일 객체뿐만 아니라 리스트도 받을 수 있습니다.

#### 예시 1: 단일 객체를 사용하는 경우

```java

Response<User> userResponse = new Response<>(true, "User found", user);


```

#### 예시 2: 리스트를 사용하는 경우

```java

List<User> users = Arrays.asList(user1, user2, user3);
Response<List<User>> usersResponse = new Response<>(true, "Users found", users);


```

### 정리

제네릭 타입으로 리스트 처리: 제네릭 타입으로 리스트(`List<T>`)를 처리할 수 있습니다. 이를 통해 단일 객체뿐만 아니라 컬렉션 타입도 처리할 수 있어 더 유연한 코드 작성이 가능합니다.

이러한 방식으로 `Response` 클래스를 유연하게 사용할 수 있습니다. 단일 객체 또는 리스트를 포함한 다양한 데이터 타입을 처리할 수 있습니다.

 

 

  • import java.util.List;: 이 한 줄로 List 인터페이스와 관련된 기능을 사용할 수 있습니다.
  • 생성자: 객체의 초기 상태를 설정하기 위해 사용됩니다. 필드를 초기화하는 데 중요한 역할을 합니다.
  • class Response<T>: 파일명과는 무관하며, 클래스가 제네릭 타입을 사용할 것임을 나타내는 문법입니다. 파일명은 Response.java로 지정합니다.

 


생성자 쓰는 이유 : 

- 초기화: 생성자를 사용하면 객체를 생성할 때 필드 값을 한 번에 설정할 수 있습니다. 이는 코드의 가독성을 높이고, 실수를 줄이는 데 도움이 됩니다.
  
- 불변 객체(Immutable Object): 객체가 생성된 후 값을 변경할 수 없게 하려면, 생성자를 사용하여 필드를 초기화하고 Setter 메서드를 제공하지 않도록 할 수 있습니다. 이렇게 하면 객체가 생성된 후에는 필드 값을 변경할 수 없으므로, 불변성을 유지할 수 있습니다.

- 필수 필드: 특정 필드가 반드시 초기화되어야 하는 경우, 생성자에서 이를 강제할 수 있습니다. 이렇게 하면 객체가 항상 올바른 상태로 생성되도록 보장할 수 있습니다.

더보기

생성자는 없어도 클래스가 동작할 수 있습니다. 그러나 생성자가 없을 경우, **Java**는 자동으로 **기본 생성자**(매개변수가 없는 생성자)를 제공합니다. 하지만 생성자가 필요한 이유와 상황에 대해 더 자세히 알아보겠습니다.

### 1. 생성자의 역할

생성자는 클래스의 인스턴스를 생성할 때 호출되며, 객체의 초기 상태를 설정하는 데 사용됩니다. 생성자를 통해 객체의 필드 값을 초기화하거나, 인스턴스를 생성할 때 필요한 초기 데이터를 전달받을 수 있습니다.

예를 들어, 다음과 같은 `Response` 클래스가 있다고 가정해 봅시다:

```java
public class Response<T> {
    private boolean success;
    private String message;
    private T data;

    // 생성자
    public Response(boolean success, String message, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
    }

    // Getter와 Setter 메서드들
}
```

이 생성자는 `Response` 객체가 생성될 때 `success`, `message`, `data` 값을 지정할 수 있게 해 줍니다.

### 2. 생성자가 없을 경우

만약 생성자를 정의하지 않으면, **Java**는 자동으로 매개변수가 없는 기본 생성자를 제공합니다:

```java
public class Response<T> {
    private boolean success;
    private String message;
    private T data;

    // 기본 생성자 - Java가 자동으로 제공
    public Response() {
    }

    // Getter와 Setter 메서드들
}
```

이렇게 되면, 객체를 생성한 후에 필드 값을 설정해야 합니다:

```java
Response<String> response = new Response<>();
response.setSuccess(true);
response.setMessage("Operation successful");
response.setData("Some data");
```

### 3. 생성자가 필요한 이유

- **초기화**: 생성자를 사용하면 객체를 생성할 때 필드 값을 한 번에 설정할 수 있습니다. 이는 코드의 가독성을 높이고, 실수를 줄이는 데 도움이 됩니다.
  
- **불변 객체(Immutable Object)**: 객체가 생성된 후 값을 변경할 수 없게 하려면, 생성자를 사용하여 필드를 초기화하고 Setter 메서드를 제공하지 않도록 할 수 있습니다. 이렇게 하면 객체가 생성된 후에는 필드 값을 변경할 수 없으므로, 불변성을 유지할 수 있습니다.

- **필수 필드**: 특정 필드가 반드시 초기화되어야 하는 경우, 생성자에서 이를 강제할 수 있습니다. 이렇게 하면 객체가 항상 올바른 상태로 생성되도록 보장할 수 있습니다.

### 4. 생성자가 없어도 되는 경우

생성자가 없어도 되는 경우는 다음과 같습니다:

- **기본 생성자만 필요한 경우**: 필드 초기화가 필요 없거나, 객체 생성 후에 Setter 메서드로 필드를 초기화해도 되는 경우.

- **단순 POJO**: 단순한 데이터 전달 객체(POJO, Plain Old Java Object)에서 필드를 기본값으로 초기화하거나, 나중에 Setter로 설정할 계획이라면 생성자를 생략할 수 있습니다.

### 결론

- **생성자가 필요하지 않은 경우**도 있습니다. 그러나 객체의 초기 상태를 명확하게 설정하고, 필요한 데이터를 강제하기 위해 생성자를 사용하는 것이 일반적입니다.
  
- **기본 생성자**가 자동으로 제공되지만, 객체를 더 명확하고 안전하게 생성하기 위해 사용자 정의 생성자를 사용하는 것이 좋습니다.

생성자를 사용하면 코드의 가독성과 안전성이 높아지고, 객체의 올바른 상태를 보장할 수 있습니다. 따라서, 상황에 따라 생성자를 추가하는 것이 바람직합니다.


제네릭 타입은 Java 언어의 기본 기능, 제네릭 사용을 위해 import 할 필요는 없음

제네릭(Generic) 타입 자체는 Java 언어의 기본 기능이므로, 제네릭을 사용하기 위해 별도로 `import`할 필요는 없습니다. 제네릭은 Java의 클래스나 인터페이스, 메소드 정의에서 타입을 일반화(Generics)하기 위해 사용하는 기능으로, 기본적으로 Java 컴파일러가 지원합니다.

### 제네릭 사용 예

예를 들어, 제네릭을 사용하는 클래스를 정의할 때:

```java
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}
```

이 클래스에서 `T`는 제네릭 타입을 나타내며, `Box` 클래스가 어떤 타입이든 담을 수 있게 만들어줍니다. `Box<String>`이나 `Box<Integer>`와 같은 구체적인 타입으로 이 클래스를 사용할 수 있습니다.

```java
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String item = stringBox.getItem();

Box<Integer> intBox = new Box<>();
intBox.setItem(123);
Integer number = intBox.getItem();
```

### 제네릭을 사용하는 클래스나 인터페이스

제네릭을 사용한 클래스를 사용할 때는 그 클래스가 어느 패키지에 속해있는지를 고려하여 `import`합니다. 예를 들어, Java에서 자주 사용되는 제네릭 클래스는 `java.util.List`와 같은 컬렉션 클래스입니다.

```java
import java.util.List;
import java.util.ArrayList;

List<String> stringList = new ArrayList<>();
stringList.add("Hello");
```

여기서 `List`와 `ArrayList`는 `java.util` 패키지에 속해 있으므로, 이를 사용하기 위해서는 해당 패키지를 `import`해야 합니다. 하지만 `T`와 같은 제네릭 타입 자체는 Java 언어의 일부이기 때문에 별도의 `import`가 필요하지 않습니다.

### 요약

- **제네릭 타입(`T`, `E`, `K`, `V` 등)**: Java의 언어 기능으로서, 기본적으로 사용 가능하며 별도의 `import`가 필요 없습니다.
- **제네릭을 사용하는 클래스나 인터페이스**: 해당 클래스나 인터페이스가 속한 패키지를 `import`해야 합니다. 예를 들어, `List`와 같은 Java 표준 라이브러리는 `java.util.List`로 `import`합니다.

따라서 제네릭 타입을 사용할 때는 `import` 선언을 신경 쓸 필요가 없습니다. 다만, 제네릭을 사용하는 클래스를 `import`할 필요가 있을 수 있습니다.

내 작업물

as-is

더보기
package egovframework.let.main.web;

import java.util.List;

import javax.naming.AuthenticationException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import egovframework.let.main.data.LoginResponse;
import egovframework.let.main.data.User;
import egovframework.let.main.service.UserService;

@RestController
@RequestMapping("/api")
public class MainController {
	
	@Autowired
	private UserService userService;
	
	@GetMapping("/help")
	public String sayHello() {
		String result="Hello eGovFramework!!";  
		return result;
	}
	
	@PostMapping("/login")
	public LoginResponse login(@RequestParam("name") String name, @RequestParam("pass") String rawPassword) {

	    // 받은 id와 pass를 콘솔에 출력
	    System.out.println("Received Name: " + name);
	    System.out.println("Received Password: " + rawPassword);
	    
	    name = name.trim();
	    rawPassword = rawPassword.trim();
	    
	    if (name.isEmpty() || rawPassword.isEmpty()) {
	        return new LoginResponse(false, "Name and password must not be null", null);
	    }
	    
	    User user = userService.userLogin(name, rawPassword);
	    
	    if (user == null) {
	        return new LoginResponse(false, "Login failed. Invalid name or password.", null);
	    }

	    return new LoginResponse(true, "Login successful", user);
	}
	
	@GetMapping("/userlist")
    public List<User> userList() {
		List<User> users = userService.getAllUsers();
		for (User user : users) {
            System.out.println("Returning User: id=" + user.getId() + ", name=" + user.getName());
        }
        return users;
    }
	
	@PostMapping("/usercreate")
    public User userCreate(@RequestParam("user_id") String userId,
                           @RequestParam("pass") String rawPassword,
                           @RequestParam("name") String name,
                           @RequestParam("etc") String etc) {
        
        User user = userService.createUser(userId, rawPassword, name, etc);
        return user;
    }

}

to-be

package egovframework.let.main.web;

import java.util.List;

import javax.naming.AuthenticationException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import egovframework.let.main.data.Response;
import egovframework.let.main.data.User;
import egovframework.let.main.service.UserService;

@RestController
@RequestMapping("/api")
public class MainController {
	
	@Autowired
	private UserService userService;
	
	@GetMapping("/help")
	public String sayHello() {
		String result="Hello eGovFramework!!";  
		return result;
	}
	
	@PostMapping("/login")
	public Response<User> login(@RequestParam("name") String name, @RequestParam("pass") String rawPassword) {

	    // 받은 id와 pass를 콘솔에 출력
	    System.out.println("Received Name: " + name);
	    System.out.println("Received Password: " + rawPassword);
	    
	    name = name.trim();
	    rawPassword = rawPassword.trim();
	    
	    if (name.isEmpty() || rawPassword.isEmpty()) {
	        return new Response<>(false, "Name and password must not be null", null);
	    }
	    
	    User user = userService.userLogin(name, rawPassword);
	    
	    if (user == null) {
	        return new Response<>(false, "Login failed. Invalid name or password.", null);
	    }

	    return new Response<>(true, "Login success", user);
	}
	
	@GetMapping("/userlist")
    public Response<List<User>> userList() {
		List<User> users = userService.getAllUsers();
		for (User user : users) {
            System.out.println("Returning User: id=" + user.getId() + ", name=" + user.getName());
        }
		return new Response<>(true, "User list retrieved successfully", users);
    }
	
	@PostMapping("/usercreate")
    public Response<User> userCreate(@RequestParam("user_id") String userId,
                           @RequestParam("pass") String rawPassword,
                           @RequestParam("name") String name,
                           @RequestParam("etc") String etc) {
        
        User user = userService.createUser(userId, rawPassword, name, etc);
        return new Response<>(true, "User created successfully", user);
    }

}