의존성 주입에 대한 추상적인 정의는 일단 접어두고 의존성 주입이라는 낯선 개념이 왜 필요한지 먼저 알아보겠습니다.
이 글을 읽기 전 선수 지식 포스팅
1. 예제 설명을 위한 준비사항(프로젝트 및 파일 생성)
클래스 라이브러리로 생성한 프로젝트 2개(ProjectA, ProjectB)를 구성합니다.
그리고 그 하위에 ProjectATest.cs, ProjectbTest.cs라는 파일을 생성하고, 아래와 같이 Test()메소드를 구현합니다.
//ProjectATest.cs
//ProjectBTest.cs
여기서 우리가 하고 싶은건 Program.cs 단에서 위 클래스들의 메소드를 사용하고 싶습니다.
일반적으로 다른 프로젝트의 메소드를 실행하려면 어떻게할까요?
아래와 같이 using을 통해 실행하고 싶은 클래스가 속한 네임스페이스를 추가 한 후
//Program.cs
객체 생성을 통해 해당 메소드에 접근하는 방법을 이용합니다.
아직까지 크게 문제가 없어보이는 코드입니다.
2. 의존성(dependency)이 높은 코드
문제는 아래와 같이 메소드가 많아졌을때 입니다.
//Program.cs
각 메소드들이 ProjectATest()의 객체를 필요로 합니다.
이런 상황에서 갑자기 ProjectATest가 아닌 ProjectBTest 메소드를 쓰고 싶다고 해보겠습니다.
그럼 이 모든 메소드의 코드들을 일일이 수정해줘야 합니다.
이러한 상황을 객체와 의존성이 높다라고 표현을 합니다.
즉 서로가 끈끈(sticky)하게 엮인 관계 이다 보니 하나가 바뀌면 다 수정해줘야 하는 거죠.
그럼 이들의 의존성을 조금 느슨하게 할 방법이 없을까요?
3. 생성자를 통해 의존성 낮추기
constructor(생성자)를 통해 의존성을 낮춰줄 수 있습니다.
보시는것 처럼 생성자에서 한번 객체를 생성한 후 전역변수를 통해 각 메소드에게 할당을 하였습니다.
만약 이전처럼 ProjectBTest의 메소드를 사용하고 싶으면 아래 빨간색 사각형 부분만 수정하면 되니 이전보다 의존성이 낮아졌다고 볼 수있는거죠.
4. 의존성 주입 개념
그런데 실제로 우리가 만나는 코드는 아래와 같은 형태를 많이 띕니다.
객체 생성을 하는 코드는 없고 뜬금 없이 해당 객체를 생성자 인자로 받는 형태이죠.
이러한 형태는 프레임워크(asp.net core, blazor 등)에서 자주 보이는 형태인데요.
프레임워크 단에서는 필요시 자동적으로 객체를 생성하여 인자로 던져주는 기능을 해주기 때문입니다.
이 던져주는 작업(?)을 의존성 주입(dependency injection)이라고 합니다.
이제야 나무위키의 추상적은 개념이 이해가 되기 시작할겁니다.
즉 현재 작동시키는 클래스 내부가 아닌 외부에서 객체를 생성한 후 그 객체를 제공하는 기술이라는 것이죠.
5. 의존성 주입 체험해보기
지금 까지 작성한 코드로 실행을 해보면 아래와 같이 CS7036에러가 뜹니다.
왜냐하면 우리는 프레임워크가 아닌 단순 c# 콘솔로 예제를 진행 중이기 때문입니다.
다시 말하면 의존성 주입을 해줄 장치가 없어 나는 에러입니다.
이 장치를 IoC Container라고 하는데요.
여기서 Ioc는 Inversion of Control의 약자로 제어의 역전을 의미합니다.
쉽게 말하면 우리가 할 역할(control)을 다른 프레임워크가 대신 해주기 때문에 control이 inversion되었다라고 표현할 수있는거죠.
(1) DependencyInjection 패키지 다운로드
//Program.cs
설치 후 ServiceCollection()이라는 클래스 객체를 생성 합니다.
해당 collection(컨테이너)안에 의존성 주입으로 사용하고자 하는 클래스를 담아주면 됩니다.
//Program.cs
ProjectATest클래스를 사용하고 싶기 때문에 <>꺽새 안에 클래스명을 넣어주면 됩니다.
이때 .AddScope메소드를 사용했는데요.
실제로 AddScoped 외에도 AddSingleton, AddTransient도 존재합니다.
이는 객체 lifetime을 어떻게 설정할지에 따라 선택지가 달라집니다. 하지만 이번 포스팅에서는 관련 설명은 생략하도록 하겠습니다.
마지막으로 DITutor클래스를 통해 메소드를 실행해볼텐데요.
이 또한 실행을 하기 위해선 DITutor 클래스를 collection(컨테이너)에 추가해 줘야 합니다.
//Program.cs
그 후 BuildServiceProvider()메소드를 통해 provider 객체를 생성하고, GetService()를 통해 DiTutor객체를 생성 후, Test1()메소드를 실행해 보았습니다.
output:
정상적으로 실행이 되네요.
여기서 ProjectBTest 클래스를 사용하고 싶다면, 아래 빨간 사각형부분을 수정해줘야 하는데요.
아직까지 수정할 부분이 많아보입니다..
이때 인터페이스를 사용하면 좀 더 의존성을 낮출 수가 있습니다.
6. 인터페이스를 통한 의존성 낮추기
ProjectATest, ProjectBTest 둘다 같은 형태임으로 인터페이스 사용이 가능합니다.
그리고 인터페이스를 각 클래스에 상속해줍니다.
기존 ProjectATest로 작성했던 부분을 IProjectTest로 수정해줍니다.
//Program.cs
다음으로 주목할 부분입니다.
기존에는 단순히 ProjectATest를 추가했다면, 수정본에서는 첫번째 자리에 인터페이스를, 두번째 자리에 ProjectATest를 넣어주었습니다.
이 코드가 의미하는것은 IProjectTest 인터페이스를 상속한 녀석들 중 어떤 녀석을 구체적으로 사용할지를 정해주는 코드라고 보시면됩니다.
이 상태에서 ProjectBTest를 사용하고 싶다면 아래부분만 변경 해주면 되겠죠?
즉, 코드 수정할 부분이 압도적으로 줄어들게 됩니다.
'c#' 카테고리의 다른 글
C#에서 파이썬 파일 실행 시키는 방법 완벽정리(with Process) (1) | 2023.01.03 |
---|---|
[c#] Entity Framework 사용 방법(code-first) (0) | 2022.08.19 |
[c#] Insert, Update, Delete 하는법 with EntityFramework (0) | 2022.07.21 |
[c#] localdb에 EntityFramework 연결 하는 방법 (0) | 2022.07.19 |
[c#] LINQ First vs Single | FirstOrDefault vs SingleOrDefault 차이 (0) | 2022.07.15 |
댓글