오늘은 메모장 프로젝트에서 발생한 객체 중복 생성 문제와 강한 결합 문제를 해결하기 위해 IoC(Inversion of Control)와 DI(Dependency Injection)를 학습했다. 이를 통해 객체의 생성을 관리하고 유지보수성을 높이는 방법을 적용할 수 있었다.
먼저, MemoService 클래스에서 MemoRepository 객체가 여러 메서드에서 중복 생성되는 문제가 있었다. 이는 코드 중복을 초래하고, 비효율적인 리소스 사용으로 이어진다.
public class MemoService {
private final JdbcTemplate jdbcTemplate;
public MemoService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// DB 저장
MemoRepository memoRepository = new MemoRepository(jdbcTemplate); // 중복 생성
// 로직...
}
public List<MemoResponseDto> getMemos() {
// DB 조회
MemoRepository memoRepository = new MemoRepository(jdbcTemplate); // 중복 생성
// 로직...
}
public Long updateMemo(Long id, MemoRequestDto requestDto) {
MemoRepository memoRepository = new MemoRepository(jdbcTemplate); // 중복 생성
// 로직...
}
public Long deleteMemo(Long id) {
MemoRepository memoRepository = new MemoRepository(jdbcTemplate); // 중복 생성
// 로직...
}
}
여기서 문제는, MemoRepository 객체가 여러 메서드에서 반복적으로 생성된다는 점이다. 이는 비효율적이며, 유지보수에 어려움을 줄 수 있다.
이 문제를 해결하기 위해 MemoService 클래스에서 MemoRepository 객체를 한 번만 생성하고, 모든 메서드에서 이를 재사용하도록 수정할 수 있다. 생성자를 통해 객체를 주입받는 방식으로, MemoRepository 객체를 재사용함으로써 중복 생성 문제를 해결할 수 있다.
public class MemoService {
// 멤버 변수 선언
private final MemoRepository memoRepository;
// 생성자 주입을 통해 MemoRepository를 한 번만 생성
public MemoService(JdbcTemplate jdbcTemplate) {
this.memoRepository = new MemoRepository(jdbcTemplate);
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// DB 저장
Memo saveMemo = memoRepository.save(requestDto);
return new MemoResponseDto(saveMemo);
}
public List<MemoResponseDto> getMemos() {
// DB 조회
return memoRepository.findAll();
}
}
이제 MemoRepository 객체는 한 번만 생성되고, 모든 메서드에서 재사용된다. 이를 통해 코드 중복이 사라지고 유지보수가 쉬워졌다.
이제 강한 결합 문제에 대해 살펴보자. 강한 결합이란, 하나의 객체가 다른 객체에 강하게 의존하여, 하나의 객체가 변경되면 다른 객체도 함께 변경해야 하는 상황을 말한다. 예를 들어, Controller1이 Service1 객체를 생성하고, Service1이 Repository1 객체를 생성하는 방식에서 Repository1의 생성자가 변경되면, Controller1과 Service1의 코드도 변경해야 한다.
public class Controller1 {
private final Service1 service1;
public Controller1() {
this.service1 = new Service1(); // Service1 직접 생성
}
}
public class Service1 {
private final Repository1 repository1;
public Service1() {
this.repository1 = new Repository1(); // Repository1 직접 생성
}
}
public class Repository1 { ... }
이 코드에서, 만약 Repository1의 생성자에 DB 접속 정보(ID, PW)를 추가해야 하는 상황이 생기면, Service1과 Controller1의 코드도 모두 수정해야 한다.
강한 결합 문제를 해결하기 위해 DI(Dependency Injection)를 사용하여 Repository1 객체를 외부에서 주입받도록 수정할 수 있다. 이를 통해 Controller1, Service1에서 Repository1의 생성을 직접 관리하지 않도록 하여, 결합을 느슨하게 만들 수 있다.
public class Service1 {
private final Repository1 repository1;
// 외부에서 Repository1을 주입받음
public Service1(Repository1 repository1) {
this.repository1 = repository1;
}
}
public class Controller1 {
private final Service1 service1;
// 외부에서 Service1을 주입받음
public Controller1(Service1 service1) {
this.service1 = service1;
}
}
이제 Repository1의 생성자가 변경되더라도 Controller1과 Service1은 변경할 필요가 없다. 객체 생성을 외부에서 주입받는 구조로 바뀌면서, 결합이 느슨해졌기 때문이다.
결과적으로, 강한 결합 문제는 느슨한 결합으로 개선되었으며, 각 객체는 외부에서 주입받아 재사용된다. 이를 통해 코드의 유지보수성과 확장성이 크게 향상되었다.
public class MemoController {
private final MemoService memoService;
// MemoService를 외부에서 주입받음
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
}
public class MemoService {
private final MemoRepository memoRepository;
// MemoRepository를 외부에서 주입받음
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
}
이제 MemoController와 MemoService는 JdbcTemplate을 주입받지 않아도 되며, 필요한 객체만 주입받아 재사용할 수 있다.
제어의 역전(Inversion of Control, IoC)이란, 프로그램의 제어 흐름이 개발자에서 프레임워크(스프링 등)로 전환되는 것을 의미한다. 즉, 객체의 생성을 개발자가 직접 관리하던 기존 방식에서, IoC를 통해 프레임워크가 객체의 생성을 관리하고 주입하는 방식으로 흐름이 역전된다. 이를 통해 더 유연한 구조와 느슨한 결합을 구현할 수 있다.
IoC와 DI를 적용함으로써, 강한 결합 문제를 느슨한 결합으로 개선할 수 있었다. 이를 통해 객체 간의 의존성을 줄이고, 코드의 유지보수성과 가독성이 크게 향상되었다. 특히, 제어의 역전을 통해 객체 생성을 프레임워크에 위임함으로써 코드가 훨씬 유연하고 효율적으로 동작하는 것을 확인할 수 있었다.