Software Engineering

프레임워크와 라이브러리의 차이점, Inversion of Control과 Dependency Injection

남희정 2023. 9. 10. 13:35

React 공식 홈페이지
Vitest 공식 홈페이지

 

프레임워크와 라이브러리의 차이점은 무엇일까? 개발자들은 흔하게 접할 수 있는 단어다. 다만 그 차이를 명확하게 한 줄로 설명할 수 있는가,,? 나는 못한다. 그래서 정리하기 위해 포스팅을 해본다!

Framework와 Library

프레임워크는 다양한 라이브러리의 모음이라고 대답하는 경우가 많다. 하지만 이 정의는 전적으로 사실이라 할 수 없다. 

정확한 구분을 위해선 "누가 누구를 호출하는가?" caller/callee를 정의해야 한다.

Framwork 🖼️

프레임워크는 사용자가 사용자 지정 애플리케이션을 만들기 위해 작성하는 개방형, 또는 구현되지 않은 함수 또는 객체를 정의한다. 프레임워크는 그 자체로 애플리케이션이기 때문에 범위가 더 넓고 사용자의 필요에 따라 사용자 애플리케이션을 만드는 데 필요한 거의 모든 것을 포함한다.

 

컴퓨터 프로그래밍에서 소프트웨어 프레임워크는 일반적인 기능을 제공하는 소프트웨어를 사용자가 작성한 추가 코드를 통해 선택적으로 변경하여 애플리케이션별 소프트웨어를 제공할 수 있는 추상화이다.

 

Library 📚

라이브러리는 애플리케이션 코드가 특정 기능을 위해 호출하는 일련의 헬퍼 함수/객체/모듈을 제공한다. 라이브러리는 일반적으로 좁은 범위(String, IO, Socket)에 초점을 맞추기 때문에 API도 더 작고 종속성이 더 적은 경향이 있다. 라이브러리는 클래스 정의의 모음이다. 코드의 재사용성, 즉 다른 개발자가 이미 작성한 코드를 사용하기 위해서이다. 

 

Inversion of Control

 핵심적인 차이점은?

제어의 역전 (Inversion of Control, IoC)

우리가 프레임워크 없이 개발할 때엔 객체의 생성, 설정, 초기화, 메소드 호출, 소멸(객체의 생명주기)을 프로그래머가 직접 관리한다. 또한 전통적인 프로그래밍에서는 외부 라이브러리를 사용할 때 개발자가 직접 외부 라이브러리를 호출하는 형태로 이용한다.

하지만 프레임워크를 사용하면 모두 프레임워크에 위임할 수 있다. 외부 라이브러리가 프로그래머가 작성한 코드를 호출하고, 흐름을 제어한다. 이처럼 외부에 위힘하는 설계 원칙을 제어의 역전이라고 한다.

프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이라고 할 수 있다.

 

쉽게 정리해보자면, 전통적 방식으로 라이브러리를 사용하는 건 우리의 프로젝트 일부분으로 라이브러리를 가져와 우리가 직접 컨트롤하는 것이지만 IoC는 우리의 코드가 프레임워크의 일부분이 되어 그에 의해 제어되는 것이다. 

 

애플리케이션의 컨트롤 책임이 프레임워크로 위임되므로 개발자는 핵심 비즈니스 로직에 더 집중할 수 있다는 장점이 있다. 

의존성 주입 Dependency Injection

IoC와 DI는 밀접한 관계가 있다. 제어의 역전은 실행의 흐름을 제어의 외부로 넘기는 개념이다. 의존성 주입은 이러한 제어의 역전을 실제로 구현하는 디자인패턴 중 하나라고 보면 된다. 

 

DI는 클래스나 객체가 필요로 하는 의존성을 외부에서 주입받는 것으로, 개발자가 직접 해당 의존성을 생성하거나 관리하지 않고, 외부에서 주입되는 방식을 사용한다. 코드는 보다 모듈화되며 유지보수하기 쉬워진다.

 

말로만 하면 어려우니 코드로 확인해보자.

프레임워크가 나의 코드를 어떻게 호출한다는걸까?

프레임워크를 사용해본 적이 없지만 대표적인 프레임워크로 Angular를 예로 설명하겠다.

 

✔️ 의존성 주입 Dependency Injection, DI

Angular에서는 컴포넌트, 서비스, 모듈 등의 요소 간에 필요한 의존성을 주입할 수 있다. 클래스나 객체가 직접 자신의 의존성을 만들거나 관리하는 것이 아니라, 외부에서 주입되는 방식이다.

 

✔️ IoC 컨테이너 IoC Container (프레임워크)

Angular는 의존성 주입을 관리하기 위한 IoC 컨테이너를 제공한다. 이 컨테이너는 애플리케이션의 구성 요소간에 의존성을 관리하고 주입하는 역할을 한다.

 

✔️ 의존성 주입 주석 Dependency Injection Annotations

Angular에서는 @Injectable, @Component, @NgModule 등과 같은 주석(Annotation)을 사용하여 DI를 정의하고 사용한다. 이 주석을 통해 컨테이너가 어떤 의존성을 주입해야 하는지 알 수 있다.

 

✔️ 의존성 주입 프로바이더 Dependency Injection Providers

Angular 모듈에서 의존성 주입 프로바이더를 설정하여 해당 모듈 내에서 사용할 수 있는 서비스나 의존성을 등록한다. 

// my-service.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root', // 애플리케이션 루트 모듈에서 서비스를 사용하도록 설정
})
export class MyService {
  getData() {
    return '이것은 서비스로부터의 데이터입니다.';
  }
}

 

1️⃣ 서비스 생성 Service Creation

먼저, 의존성 주입을 사용하기 위해 서비스를 생성한다.

// my-component.component.ts

import { Component } from '@angular/core';
import { MyService } from './my-service.service'; // 서비스 임포트

@Component({
  selector: 'app-my-component',
  template: `
    <div>
      {{ serviceData }}
    </div>
  `,
})
export class MyComponent {
  serviceData: string;

  constructor(private myService: MyService) { // 서비스를 의존성 주입으로 받음
    this.serviceData = this.myService.getData(); // 서비스 메서드를 호출하여 데이터 가져옴
  }
}

2️⃣ 컴포넌트 생성 Component Creation

다음으로, 컴포넌트에서 이 서비스를 사용하려면 의존성 주입을 설정한다.

// my-component.component.ts

import { Component } from '@angular/core';
import { MyService } from './my-service.service'; // 서비스 임포트

@Component({
  selector: 'app-my-component',
  template: `
    <div>
      {{ serviceData }}
    </div>
  `,
})
export class MyComponent {
  serviceData: string;

  constructor(private myService: MyService) { // 서비스를 의존성 주입으로 받음
    this.serviceData = this.myService.getData(); // 서비스 메서드를 호출하여 데이터 가져옴
  }
}

3️⃣ 모듈 설정 Module Confuguration

마지막으로, 애플리케이션 모듈에서 서비스를 프로바이더로 등록한다.

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyComponent } from './my-component.component';
import { MyService } from './my-service.service'; // 서비스 임포트

@NgModule({
  declarations: [MyComponent],
  imports: [BrowserModule],
  providers: [MyService], // 서비스를 프로바이더로 등록
  bootstrap: [MyComponent],
})
export class AppModule {}

MyService 서비스가 MyComponent 컴포넌트에 주입되고, 컴포넌트에서 서비스 메서드를 사용하여 데이터를 가져온다. 

 

장, 단점 🤔

Framwork 🖼️

▪️ 애플리케이션의 한 부분을 변경해도 전체 애플리케이션에 영향을 미치지 않는다.

▪️ 네트워크 트래픽에 대한 캐싱 및 최적화된 프로세스 제공

▪️ 더 적은 코드로 더 빠른 개발 

▪️ 크로스 플랫폼 애플리케이션 개발 지원

▪️ 풍부한 동적 콘텐츠 생성, UX 우수

▪️ Angular와 같은 일부 JS 프레임워크는 MVC를 기반으로, 데이터 바인딩을 사용한다.

MVC(Model-View-Controller)패턴은 소프트웨어 개발에서 주요한 디자인 패턴 중 하나로, 소프트웨어의 비즈니스 로직과 화면을 구분하는 데에 중점을 둔다. (관심사의 분리)

데이터 바인딩은 컴포넌트 간의 연결을 통해 데이터를 자동으로 동기화하는 매커니즘이다. 

▫️ 프레임워크의 편리함으로 인해 프로그래머는 프로그래밍 언어를 깊이 있는 이해 없이 사용할 수 있음

▫️ 기능을 조정할 수 있는 옵션이 제한적이다.

▫️ 애플리케이션의 규모에 적합한 프레임워크를 선택해야 성능과 UX에 대해 긍정적인 평가를 받을 수 있다.

▫️ MVC에서 비즈니스 로직과 프레젠테이션 레이어(뷰 & 컨트롤러)를 명확하게 분리하는 것이 어려울 수 있음

▫️ 모든 버전에서 새로운 기능, 사용되지 않는 기능을 최신 상태로 유지해야된다.

 

종류 : 웹개발 PHP(CodeIgniter, Laravel), Python(Django), JavaScript(Angular) 

Library 📚

▪️ 런타임 중에 컴파일러가 라이브러리를 선택적으로 포함시켜 프로그램의 성능을 향상시킨다.

▪️ 함수를 명시적으로 정의하지 않고도 코드 내에서 참조할 수 있는 재사용 가능한 함수를 제공한다.

▪️ 복잡한 함수에 대한 코드를 작성할 필요가 없음

▪️ 동일한 문제를 반복해서 해결하기 위해 코드를 작성할 필요가 없음

▪️ HTTP 및 라우팅과 같은 글로벌 상태 관리에 대한 걱정 없이 라이브러리가 구현하는 기능에만 집중할 수 있다.

▪️ 원하는 라이브러리를 선택할 수 있다.

▪️ 애플리케이션 개발 비용 절감

▪️ 프로그래머가 구성 가능하고 재사용 가능한 라이브러리 개발에 집중할 수 있도록 지원함.

▪️ 여러 환경과 사용 사례에 대해 사전 테스트된 코드 제공

▫️ 라이브러리를 사용한다는 것은 코드가 해당 라이브러리에 묶여 있다는 것을 의미한다.

▫️ 지원되지 않는 환경에서 라이브러리를 사용하려면 래퍼가 필요하므로 애플리케이션의 성능에 영향을 미칠 수 있다.

▫️ 새 버전의 애플리케이션과 호환성 문제가 발생할 수 있다.

▫️ 여러 라이브러리를 사용하면 종속성 충돌로 인해 성능에 부정적인 영향을 미칠 수 있음.

 

 

라이브러리와 프레임워크의 차이점을 한마디로 정리하자면, 개발자가 코드에 대한 제어권을 갖느냐 위임하느냐 겠다.


[Software Framework vs Library]

[The Difference Between a Library and a Framework]

[Library patterns Why frameworks are evil]

[The Difference Between a Framework and a Library]

[Dev: Framework와 Library는 무엇이고, 어떤 차이가 있을까]

[IoC, DI 무엇일까]

[제어의 역전 (Inversion Of Control, IoC)]
[
[DI] Dependency Injection 이란?]

[Framework Vs Library]

[React 프레임워크가 아니라 라이브러리일까?]