반응형
Spring과 자바의 2가지 인스턴스 생성방법의 차이를 알아보자
내가 완전 1개월 차 주니어 시절 스프링을 통해 개발을 하던 도중 컨트롤러 메서드를 작성할 일이 생겼는데 이때 메서드의 매게 변수로 객체를 받는 것과 메서드 내부에서 new를 통해 객체를 생성해서 사용하는 것에 대한 궁금증이 생겼다. 10년 이상된 프로젝트였는데 어떤 객체는 new를 통해 생성하였으며 어떤 객체는 Map이나 List를 통해 파라미터로 받아서 사용하고 있었다. 어떤 방식이 더 좋은 방법인지 간단하게 알아보도록 하자
1. Spring에서의 DI (Dependency Injection) 이해
1-1. Spring에서의 DI(Dependency Injection) 이해하기
Spring Framework에서 DI(Dependency Injection)를 이해하려면, 먼저 '의존성'이 무엇인지 파악하는 것이 중요하다. 의존성이란 간단히 말해서 한 객체가 다른 객체의 기능이나 데이터에 의존하는 것을 말한다.
예를 들어, 내가 만든 UserService 클래스가 UserRepository 클래스를 사용해야 할 경우, UserService는 UserRepository에 의존성을 가지고 있다고 볼 수 있는것이다.이렇게 클래스 간에 서로 필요로 하는 관계가 형성되면, 코드는 점점 복잡해지고, 변경에 취약해진다. 이런 문제를 해결하기 위해 Spring은 DI라는 기법을 사용한다. DI는 말 그대로 필요한 의존성을 외부에서 '주입'해주는 방식이다.
즉, UserService가 직접 UserRepository를 생성하거나 관리하지 않고, Spring Framework가 알아서 필요한 UserRepository 객체를 UserService에 제공해주는 것이다.
1-2. DI의 장점과 Spring에서의 활용
이런 DI 방식의 가장 큰 장점은 코드의 유연성과 테스트 용이성이 증가한다는 것이다.
UserService가 UserRepository의 구체적인 구현에 대해 알 필요가 없으니, 나중에 UserRepository의 구현이 바뀌어도 UserService 코드를 변경할 필요가 없다. 또한, 테스트할 때 실제 UserRepository 대신에 가짜(mock) 객체를 쉽게 주입할 수 있어서, 더 간단하고 독립적인 테스트가 가능해진다.
Spring에서는 이런 DI를 관리하기 위해 'Bean'이라는 개념을 사용한다.
Bean이란 Spring이 관리하는 객체로, Spring 컨테이너가 알아서 객체의 생명주기를 관리하고, 필요한 곳에 적절한 객체를 주입해주는 것이다. 이렇게 Spring이 제공하는 DI를 활용하면, 개발자는 객체 간의 복잡한 의존성을 직접 관리하는 대신, 더 중요한 비즈니스 로직 개발에 집중할 수 있다.
2. 인스턴스를 생성할때 new 키워드와 DI 패턴의 주요 차이점
2-1. 객체 생성의 책임
- new 키워드를 사용하면 객체 생성 책임이 개발자에게 있다. 반면, DI를 사용하면 이 책임이 Spring 컨테이너로 이동한다. 이는 개발자가 구현 세부 사항보다는 인터페이스에 초점을 맞출 수 있게 한다.
2-2. 테스트의 용이성
- new를 사용한 코드는 강한 결합을 가지고 있어 테스트가 어려울 수 있다. 그러나 DI를 사용하면 모의 객체를 쉽게 주입할 수 있어 테스트가 간편해진다.
2-3. 유연성과 유지보수
- new를 사용하면 구체적인 클래스에 의존하게 되지만, DI를 사용하면 인터페이스에 의존하므로 유연성이 높아지고 유지보수가 쉬워진다.
따라서, Spring Framework를 사용하는 경우에는 가능한 한 DI를 사용하는 것이 좋다. 그러나 모든 객체를 빈으로 만들 필요는 없다. 예를 들어, 상태를 가지지 않는 유틸리티 클래스나, 도메인 객체와 같이 생명주기를 Spring 컨테이너가 관리할 필요가 없는 객체는 new 키워드를 사용하여 직접 생성할 수 있다.
3. 메서드에서 필요한 객체를 매개변수로 받는 게 좋을까? 내부에서 new로 선언하는 게 좋을까?
3-1. Map, List, Set 같은 컬렉션 타입은 new를 사용해서 객체 생성해도 괜찮을까?
- HashMap, ArrayList와 같은 Java의 표준 컬렉션 클래스들은 상태를 가지지 않는 유틸리티 클래스나 도메인 객체와 같이 생명주기를 Spring 컨테이너가 관리할 필요가 없는 객체들이다. 이런 경우에는 new 키워드를 사용하여 직접 생성하는 것이 일반적이다.
HashMap, ArrayList같은 컬렉션 타입의 클래스는 내가 작성하고자 하는 메서드의 매개변수로 선언하고 그 매개변수를 메서드 내부에서 선언하여 사용하는 게 좋은 방법일까 아니면 메서드 내부에서 new 키워드로 새로운 HashMap, ArrayList 객체를 생성해서 사용하는 게 좋은 방법일까? 지금부터 알아보도록 하자
3-2. 아래의 예시코드를 보자
// nameList에 값을 넣어서 save()메소드로 리스트의 값을 저장하는 컨트롤러 로직이다.
@ResponseBody
@GetMapping("/)
public String getPage(List<String> nameList) {
nameList.add("data1");
nameList.add("data2");
userService.save(nameList);
return "ok";
}
- 위와 같이 작성된 상황에서는 nameList를 메서드의 매개변수로 받는 것이 아니라, 메서드 내부에서 new ArrayList<>();를 사용하여 nameList를 생성하는 것이 더 적절하다. 왜냐하면, nameList 객체는 getPage() 메서드 내부에서만 사용되며, 이 메서드의 사용자가 nameList를 제공할 필요가 없기 때문이다.
- 또한, nameList 객체를 메서드의 매개변수로 받게 되면, 이 메서드를 사용하고자 하는 사용자가 nameList의 상태에 대해 알아야 하므로(파라미터로 넣어줘야만 한다.) 메서드의 사용성이 저하될 수 있다.
따라서, 코드를 다음과 같이 수정하는 것이 좋다.
@ResponseBody
@GetMapping("/")
public String getPage() {
// new가 보기싫어도 얘들은 이렇게 작성하는게 좋다고 한다.
List<String> nameList = new ArrayList<>();
nameList.add("data1");
nameList.add("data2");
userService.save(nameList);
return "ok";
}
- 이렇게 하면 getPage 메서드는 nameList의 생성과 관리를 스스로 담당하며, 이 메서드의 사용자는 nameList에 대해 알 필요가 없다.(파라미터로 넣어줄 필요가 없어졌다.) 이로 인해 코드가 더 간결하고 명확해진다.
3-3. 결론
메서드 내부에서만 사용되는 컬렉션 타입 객체를 메서드의 매개변수로 받는 것은 일반적으로 권장되지 않는다. 이런 경우에는 메서드 내부에서 new 키워드를 사용하여 컬렉션을 직접 생성하는 것이 더 간결하고 명확하다. 또한, 이렇게 하면 메서드의 사용자는 메서드가 어떤 컬렉션을 사용하는지 알 필요가 없으므로 메서드의 사용성이 향상된다.
따라서, 메서드 내부에서만 사용되는 컬렉션을 메서드의 매개변수로 받는 것은 일반적으로 좋지 않은 설계이다. 대신, 메서드 내부에서 new 키워드를 사용하여 컬렉션을 직접 생성하고 사용하는 것이 좋다.
4. 한 메서드 안에서 컬렉션 타입의 인스턴스를 new 로 많이 생성해도 성능에 문제가 없는가?
4-1. 성능 영향
ArrayList나 HashMap과 같은 컬렉션 인스턴스를 new 키워드를 사용하여 생성하는 것은 일반적인 Java 프로그래밍의 일부이다. 이러한 객체 생성은 JVM의 힙 메모리에 공간을 할당하는 작업을 포함하며, 객체가 더 이상 필요하지 않게 되면 가비지 컬렉터에 의해 자동으로 회수된다.
일반적으로, 이러한 객체 생성과 가비지 컬렉션은 성능에 큰 영향을 미치지 않는다. JVM은 객체 생성과 가비지 컬렉션을 매우 효율적으로 처리하도록 설계되었으며, 대부분의 경우에는 이러한 작업이 애플리케이션의 성능에 미치는 영향을 거의 느끼지 못한다.
4-2. 특수 상황의 성능 고려
그러나 매우 많은 수의 객체를 짧은 시간 동안 생성하거나, 매우 큰 객체를 생성하는 경우에는 성능에 영향을 미칠 수 있다. 이러한 경우에는 객체 풀링과 같은 기법을 사용하여 객체 생성의 비용을 줄이거나, 더 효율적인 자료 구조를 사용하여 메모리 사용량을 줄일 수 있다.
4-3. 결론
따라서, ArrayList나 HashMap과 같은 컬렉션 인스턴스를 new로 생성하는 것이 성능에 미치는 영향은 대부분의 경우에는 무시할 수 있을 정도이다. 그러나 특정 상황에서는 이러한 객체 생성이 성능에 영향을 미칠 수 있으므로, 성능이 중요한 애플리케이션에서는 적절한 성능 테스트와 최적화를 수행해 보는 것이 좋다.
자바 리플렉션이 궁금하다면!
반응형
'Spring 기초 > Spring 기초 지식' 카테고리의 다른 글
SpringBoot: 인터셉터(interceptor)의 동작원리 (0) | 2023.08.13 |
---|---|
Spring MVC의 Model, ModelAndView, ModelMap 비교 (0) | 2023.08.13 |
스프링에서의 데이터베이스 접근 방법: DAO, Mapper, 그리고 @Mapper 어노테이션 사용법 (0) | 2023.08.09 |
스프링에서 데이터 전달의 핵심: VO와 DTO의 이해 및 활용 (0) | 2023.08.09 |
Spring Boot 폼 데이터 바인딩: @ModelAttribute 활용법 (0) | 2023.08.09 |