TOY프로젝트를 진행하며 DTO 클래스 파일의 개수가 너무 많아져 관리하기가 어려운 문제가 발생했습니다. 하나의 도메인에 생기는 여러개의 DTO를 한번에 관리하는 방법이 없을까..?라는 고민을 하게 되었습니다. 관련 자료를 찾아보니 많은 분들이 Inner class를 통해 DTO를 관리하는 방식을 사용하며 개발의 편의성을 향상시켰다는 사실을 알게 되었습니다. 왜 Inner class를 사용하는지 주의사항은 무엇인지 관련내용을 공부하며 정리하려 합니다.
참고
https://johngrib.github.io/wiki/java-inner-class-may-be-static/
구조
public class User{
class admin{
}
}
내부 클래스는 말 그대로 중첩클래스 중 내부에 선언된 클래스입니다.
하지만 위의 예시와 같이 중첩클래스를 선언하면 다음과 같은 오류 하나가 발생합니다.
Reports any inner classes which may safely be made static. An inner class may be static if it doesn't reference its enclosing instance.
A static inner class does not keep an implicit reference to its enclosing instance. This prevents a common cause of memory leaks and uses less memory per instance of the class.
해당 내용을 번역하자면 다음과 같습니다.
안전하게 static 으로 만들 수 있는 내부 클래스를 발견했습니다. 만약 내부 클래스가 자신의 바깥 클래스 인스턴스를 참조하지 않는다면, 내부 클래스는 static으로 선언해도 됩니다.
static 내부 클래스는 자신을 둘러싸는 바깥 클래스 인스턴스의 임시적 참조를 유지하지 않습니다. 즉 내부 클래스를 static으로 선언한다면 메모리 누수를 예방할 수 있고, 클래스의 각 인스턴스당 더 적은 메모리를 사용하게 됩니다.
수정한 구조
public class User{
static class admin{
}
}
해당 오류를 다시 한번 정리하자면 위와 같은 구조로 사용할시 static을 선언하지 않았을때보다
1. 메모리 누수 예방
2. 적은 메모리 사용
3. 생성시간 단축
이라는 이점이 있다는 말입니다.
단순히 static이 붙어있냐 없냐의 차이인데 왜 이와 같은 이점이 생길까요??
사실 이 static 유무의 의미상 차이는 생각보다 큽니다.
static이 선언되어있지 않은 멤버 클래스는 자신을 둘러싼 바깥 인스턴스와 암묵적으로 연결이 되어 있습니다. 때문에 바깥 인스턴스 없이는 멤버 클래스를 생성할 수 없게 됩니다.(클래스명.this)
하지만 중첩 클래스의 내부 클래스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 굳이 바깥 인스턴스를 통해서 생성하고 사용할 필요가 없습니다. 때문에 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어 바깥 인스턴스로의 외부참조를 가지지 않게 합니다. 이렇게 된다면 당연히 외부 참조를 저장할 메모리가 사라지니 메모리사용이 줄어들고 생성 시간이 감소됩니다.
또한 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다는 문제 예방가능
기존에 사용했던DTO
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserJoinRequestDTO {
private String name;
}
@Getter
@Builder
@AllArgsConstructor
public class UserJoinResponseDTO {
private Long id;
private String name;
}
기존에 제가 사용하는 방식을 위와 같았습니다. 요청별로 DTO를 외부클래스로 나누어 관리하는 구조입니다. 하지만 요청별로 DTO를 관리하다보니 너무 많은 클래스 파일이 생성돼 관리하기가 어려웠습니다.
내부 클래스 적용
public class UserDto{
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class UserJoinRequestDTO {
private String name;
}
@Getter
@Builder
@AllArgsConstructor
public static class UserJoinResponseDTO {
private Long id;
private String name;
}
}
내부 클래스를 적용하여 클래스 파일의 개수를 줄이고 UserDto하나로 관련된 Dto를 묶음으로서 빠르게 원하는 dto를 찾을 수 있습니다.