프레임워크와 라이브러리의 차이

SeongHo Hong
7 min readDec 26, 2019

--

프레임워크와 라이브러리라는 단어는 나에게 익숙한 단어다. 내가 필요할 때 가져다 쓰는 도구 정도로 생각하고 있었는데, 두 개념은 다른점이 있다. 오픈소스의 README를 살펴보면 딱히 두 용어의 구분 없이 사용하고 있다. 그럴 때는 그저 ‘도구'라는 의미로 사용한다고 보면 될 것 같다. 이 글에서는 조금 더 원론적으로 접근하고자 한다. 두 용어를 비교하면서 차이점을 드러내고, 그 차이점에서 배울 점이 분명 있을 것이다.

라이브러리

당신의 코드가 라이브러리를 직접 호출한다 (참고)

라이브러리를 먼저 말하는 것은 흔히 우리가 원할 때 마음대로 사용할 수 있는 ‘도구’ 라는 개념에 가깝기 때문이다. 라이브러리를 사용하면 클라이언트가 직접 라이브러리에 요청해서 결과를 받아온다. 마틴파울러 블로그에 있는 라이브러리 예제는 다음과 같다.

#ruby
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)

언제 질문을 할 것인지, 언제 응답을 받을 것인지, 언제 결과를 처리할 것인지 내 코드에서 결정한다. 라이브러리를 사용하면 제어권이 내 코드에 있는 것이다. process_name()이 코드의 흐름과 순서를 결정하지 않는다.

프레임워크

당신의 코드는 프레임워크에게 호출 당한다. (참고)

위의 그림을 보고 조금 이상하다는 생각을 할 수도 있겠다. 내 코드를 다른 곳에서 호출한다는게 어떤 말일까? 절차적인 프로그래밍을 처음 배울 때는 생각하지 못하던 개념이다. 절차지향에서는 무엇이든 내가 제어하는 방식이었기 때문이다. 놀랍게도 프레임워크는 내 코드에 메세지를 전달한다. 또 다시 마틴파울러의 예제를 살펴보자.

require 'tk'
root = TkRoot.new()
name_label = TkLabel.new() {text "What is Your Name?"}
name_label.pack
name = TkEntry.new(root).pack
name.bind("FocusOut") {process_name(name)}
quest_label = TkLabel.new() {text "What is Your Quest?"}
quest_label.pack
quest = TkEntry.new(root).pack
quest.bind("FocusOut") {process_quest(quest)}
Tk.mainloop()

나는 윈도우 시스템에 익숙하지 않아서 위의 코드가 처음에는 조금 읽기 어려웠다. 위의 코드에서 주목해야할 점을 볼드 표시했다.

  1. 프레임워크에서 제공하는 틀에 코드를 채워넣는다.
    name.bind(“FocusOut”) {process_name(name)}
  2. 프레임워크에 제어권을 넘긴다.
    Tk.mainloop()
  3. 프레임워크가 원하는 타이밍에 bind 되어 있는 내 코드를 호출한다.

특정 도구를 사용할 때 이렇게 내 코드가 호출되는 입장에 있다면, 프레임워크를 사용하고 있다고 이해하면 될 것이다.

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

내가 호출당하는 입장이 되는 것을 제어의 역전이라고 한다. 프레임워크와 라이브러리를 명확하게 나눠주는 개념이다. 할리우드 원칙으로 설명되기도 한다. “Don’t call us, we’ll call you” 라는 말이 프레임워크의 그림과 정확히 맞아떨어진다. 내 코드를 등록해두고 호출되기를 기다리는 것이다. 어떤 시기에 호출될지는 프레임워크가 결정한다.

코드를 담는 방식

IoC에서 클라이언트 코드를 담는 방식은 다양하다. 동일한 것은 코드를 담을 수 있는 추상적인 공간이 필요하다는 것이다. 서브클래싱을 통해서 클라이언트 코드를 담기도 하고, 람다 방식 처럼 함수 자체를 argument로 플러그인해서 코드를 담기도 한다. 템플릿 메소드를 통해 코드를 담기도 한다. 다양한 방식으로 코드가 담길 수 있으니, 형태가 어떤지 보다도 제어권이 어디에 있는지 보는 것이 더 정확할 것이다.

안정적 확장, 재사용성 증가

This inversion of control gives frameworks the power to serve as extensible skeletons.(link)

IoC가 일어나면, 확장 가능한 뼈대 역할을 할 수 있도록 해준다. 언뜻 생각하면 클라이언트가 주도권을 가지고 전체를 제어하는 기존의 방식이 더 확장성이 좋다고 생각된다. 그런데 기존의 제어 방식대로 확장되면 안정적인 확장이 어렵다.

관심사 분리

우리는 관심사(오브젝트의 생성방법, 담당 기능)를 분리하고 책임(오브젝트의 생성 및 제공)을 분리하기 위해 도입했던 DaoFactory로 인해서 자연스럽게 제어의 역전을 적용했다. (link)

시스템을 제어하는 로직을 위임했기 때문에, 뼈대에서 제공하는 부분에만 집중할 수 있다. 흐름을 제어하는 순서, 그 과정에서 발생하는 객체 생성에 대해서는 신경쓰지 않게 된다.

iOS에서의 예시

iOS 앱개발에서는 UI 프레임워크를 주로 다루기 때문에 이러한 역전의 개념이 많이 적용되어 있다. 흔히 사용하는 UIViewController 같은 경우에는 화면의 생명주기에 맞게 호출되는 함수들이 있다. 그 함수들에 원하는 동작을 심어두면 타이밍에 맞춰서 코드가 실행되는 것이다. 화면이 나타나고 사라지는 시점은 UI 조작을 통해서 시작되고, 프레임워크가 그 동작을 읽어서, 내 코드가 담겨 있는 곳까지 실행시켜주는 방식이다. 아마 라이프 사이클이 있는 UI 다루는 프레임워크들은 대부분 비슷하지 않을까 추측해본다.

https://developer.apple.com/documentation/uikit/uiviewcontroller

Dependency Injection

몇몇 플랫폼에서 IoC Container라는 용어를 사용하면서 IoC에 대한 개념이 혼란스러워졌다. 좀 더 명확한 개념을 사용하기 위해 Depedency Injection을 사용하게 되었다. 프레임워크가 호출할 코드를 Depedency로 보고, 그 코드를 심는 것을 Injection이라고 표현한 것 같다. 이런 맥락에서 IoC와 DI 라는 용어가 다른 것이 아니라 좀 더 구체적으로 표현한 것으로 보면 되겠다.

정리

  • 라이브러리: 내 코드가 호출함
  • 프레임워크: 내 코드가 호출당함
  • Inversion of Control: 제어권이 프레임워크로 넘어간 상황
  • Dependency Injection: IoC의 조금더 구체적인 용어로, 제어역전을 위해 코드를 추상적인 공간에 넣는 것을 의미함

참고

http://javaj2eetechy.blogspot.com/2015/11/java-framework.html
https://webclub.tistory.com/458
https://vandbt.tistory.com/43
https://vandbt.tistory.com/44
https://martinfowler.com/bliki/InversionOfControl.html
https://www.martinfowler.com/articles/injection.html
http://www.laputan.org/drc/drc.html
https://pu-li.tistory.com/46

--

--

SeongHo Hong

Software Engineer 🧑‍💻https://github.com/cozzin