Framework VS Library 차이가 무엇일까??


개발관련공부를 하다보면 한번쯤 의문을 가질 수 있을것이다.

 

라이브러리는 알겠는데 프레임워크는 무엇인지 잘 모르겠다..

 

 

혹은 프레임워크는 알겠는데 라이브러리는 뭔지 모르겠다..

 

 

내가 생각할 때는 우선 크기가 다르다고 생각을 한다.

 

 

프레임워크 자체는 환경적인 것이라 생각하고 라이브러리는 환경보다는 작은 내가 마음대로 수정하고 이용할 수 있는 것으로 생각한다.

 

즉 환경이란 것은 말 그대로 구성되어있기 때문에 특별하게 사람이 수정할 수 없고 주어진 환경에서 활동할 수 있다.

 

그리고 라이브러리라는 것은 주머니 속 물건처럼 내가 필요할 때마다 꺼내고 사용할 수 있으며 수정할 수 있다.

 

여기까지가 내 생각이고 이후 정의를 한번 살펴보자.

 

프레임워크 (Framework)

프레임워크는 Frame + work로 합성어다.

Frame(틀)을 Work(일하다) 이용한다. 즉 틀을 가지고 활용한다 사용한다라고 볼 수 있다.

 

여기서 틀은 레고라고 생각하는 것도 좋다고 본다. 레고는 블록이라는 틀로 구성되어있으며 특정 모양을 가지고 여러 결과물(집, 차 등등)을 조립하여 만들어 볼 수 있다.

 

즉 레고 자체의 모양을 직접 바꿀 수 없게 틀로 구성된 자원을 가지고 특정 규칙을 통해(레고를 조립하는 방법) 결과물을 만들어 내는 것이다.

 

그래서 나는 이것을 환경이라고 외우고 있다.. 받아들이기 편한 비유법을 통해 이해하고 넘어간다면 오랫동안 기억할 수 있을 것이다.

 

그래서 이러한 Framework는 Spring, Spring Boot, Django 등이 존재한다.

 

해당 환경에서 특정 요소를 가지고 여러 결과물들을 만들 수 있기 때문이다. 다만 Spring이라는 틀에서 제공하는 요소 자체는 변경이 불가능하다.

 

라이브러리 (Library)

라이브러리는 그나마 친숙할 것이라 생각한다.

 

C, C++, Java, Python 등의 프로그래밍 언어를 공부하다 보면 공통적으로 코드 상단의 가장 처음 불러오는 것이 있다.

 

#include <stdio.h> // 스튜디오라는 헤더
#include <math.h> // 매스라는 헤더

int main()
{
  printf("라이브러리는 무엇일까?");
  return 0;
}

 

우리는 구현을 진행하며 이러한 header file 속의 함수를 가져와서 사용할 수 있고 또한 이러한 header file을 직접 만들어서 사용할 수도 있다.

 

/* header file */
#include <stdio.h>

# define SIZE 1024
# define MAX_NUM 2147483647
# define MIN_NUM -2147483648

typedef struct    n_list
{
  void        *data;
  struct    n_list    *next;
}
그럼 이 헤더는 라이브러리인가??

 

또 그건 아니다.

 

 

결과를 먼저 말하자면 라이브러리는 기계어로 번역된 바이너리이며 헤더 파일은 컴파일러가 컴파일하기 전 프로그래머가 이해할 수 있는 언어이고 컴파일러가 이런 헤더 파일들을 가지고 심볼네임을 만들고 후에 오브젝트파일이 생성된다. 이후 링커가 심볼네임을 가지고 라이브러리를 찾아 링크시키게 된다.

 

 

즉 헤더가 여러 개 모이는게 라이브러리가 아니며 라이브러리는 컴파일된 바이너리이므로 소스파일의 컴파일된 오브젝트파일들을 여러개 묶어놓은 것이 라이브러리이다.

 

오브젝트 파일은 리눅스, 맥에서는 (. o 파일로) 윈도우에서는 (. obj) 파일로 표현된다.

 

라이브러리는 리눅스의 경우 .a 로 표현되고 윈도우의 경우에는 .dll 로 표현된다.

 

그리고 간혹 맥에서 .dylib라는 파일도 있는데 이것은 동적 라이브러리라고 불리며 윈도우에서의 .dll과 유사하다.

 

자 여기까지 라이브러리를 헤더 파일을 통해 설명해보려고 했으며 이해하지 못해도 괜찮다! 사실 한 줄만 기억해도 상관없으며 후에 컴파일 과정을 꼼꼼하게 공부한 후 다시 한번 읽어본다면 조금 더 깊이 이해할 수 있을 것이다.

 

라이브러리는 내가 마음대로 수정하고 이용할 수 있는 것이라고 말했다.

 

라이브러리는 프레임워크보다는 작은 단위로 도구 모음 들이며 이러한 하나의 도구와 같은 것을 직접 만들 수도 수정할 수도 사용하지 않을 수도 있다.

 

라이브러리는 도구의 모음이다.

 

프레임워크라는 환경 속에서 해당 프레임워크의 규칙을 지키며 아무 라이브러리나 불러서 사용할 수 있다.

 

나는 산이라는 프레임워크 속에서 나무를 베고 싶은데 (톱, 도끼)라는 라이브러리를 이용하겠다.

 

로 말할 수 있다.

 

엄밀히 따지자면 더 깊은 개념이 있다고 생각이 있으며 틀린 말도 있을 수 있다고 생각한다.

 

내가 아직 공부한 것이 부족할 수 있기 때문에..

 

그래도 이 두 가지 개념이 헷갈렸던 사람들은 이 글을 보고 조금 더 쉽게 이해하고 넘어갔으면 좋겠다.

'지식 창고' 카테고리의 다른 글

MSA란 무엇일까?  (0) 2023.01.10
REST API란?  (0) 2022.09.12
HLS protocol 이란 무엇일까?  (2) 2022.09.11

JAVA-직렬화란?


Serializable(직렬화)

  • 직렬화(Serializable)는 무엇인가?
    • 직렬화는 자바 시스템 내부에서 사용되는 객체 또는 데이터들을 외부 자바 시스템에서도 사용할 수 있도록 바이트(byte)형태로 데이터를 변환하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 역직렬화를 포함합니다.
    • 시스템적으로 JVM의 Runtime Data Area(Heap and Stack Area)에 상주하고 있는 객체 데이터를 바이트 형태로 변환하는 기술과 직렬화된 바이트 형태의 데이터를 객체로 변환해 JVM으로 상주시키는 형태를 말하기도 합니다.
  • 그렇다면 이런 직렬화는 어떤 경우에 사용되는 것일까?
    • 생성한 객체를 가지고 파일에 저장할 때
    • 저장한 객체를 읽어올 때
    • 다른 서버에서 생성된 객체를 받을 때

즉 우리가 만든 클래스가 파일에 읽거나 쓰고, 다른 서버로 보내거나 받을 경우 반드시 이 직렬화를 이용하기 위해 Serializable interface를 implements해주어야 합니다.

public class Temp implements Serializable{
  //다음과 같은 방식으로 implements하여 사용한다.
}

이러한 Serializable interface를 구현하는 클래스들을 확인해보면 serialVersionUID라는 값이 존재합니다.

이 serialVersionUID는 필수값은 아니지만 호환 가능한 클래스는 serialVersionUID 값이 고정되어있습니다.

serialVersionUID가 선언되어 있지 않으면 클래스는 기본 해쉬값을 사용합니다.

public class HashMap<K, V> extends TempMap<K,V>
  implementes Map<K,V>, Serializable{
  private static final long serialVersionUID = 123456789L;
}

이런식으로 static final long 으로 선언하고 변수명 역시 serialVersionUID로 선언해야 자바에서 인식합니다.

이러한 UID의 값은 언제 사용될까요?

처음 Serializable의 정의쪽을 기억해봅시다. "다른 서버로 보내거나 받을 경우"어떠한 클래스를 보내고자 할 때 받는 쪽 서버에도 역시나 동일한 클래스를 가지고 있을겁니다. 그러나 새로받은 클래스와 원래 존재했던 클래스가 단순히 이름만을 가지고 자바가 같다고 파악하지 않고 위에서 설명한 이 serialVersionUID를 통해 버전을 확인하고 두 버전이 동일 할 경우에만 같은 클래스로 인식하게 됩니다. 또한 UID가 같더라도 클래스 내부 변수의 개수나 타입등이 다르다면 이 경우에도 다른 클래스라 인식합니다.

Reference

'JAVA' 카테고리의 다른 글

Java - Logging 이란?  (1) 2024.11.12
[JAVA & Database] JDBC, MyBatis, JPA 의 차이점  (1) 2022.09.26
[Java] 언어의 특징  (2) 2022.09.09
[JAVA]DAO, DTO, VO 정리.  (0) 2021.12.25

객체 지향 설계의 5가지 원칙(SOLID)


로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리

  • 프로그래머가 시간이 지나도 유지 보수확장이 쉬운 시스템을 만들고자 할 때 이 원칙들을 함께 적용할 수 있다.
  • 이 5가지 원칙은 애자일(Agile) 소프트웨어 개발 과 적응적소프트웨어 개발의 전반적 전략의 일부다.

  • SRP : 단일 책임 원칙 (Single responsibility principle)

    • 단일 책임 원칙이란 OOP적인 관점에서 생각을 해보았을 때 한 클래스의 변경사항이 발생해도 다른 클래스 혹은 코드상의 다른쪽의 변경점이 적다면 단일 책임 원칙이 잘 지켜지고 있다고 생각해볼 수 있다. 즉 상속관계를 예를 들었을 경우 부모클래스의 정보 수정만으로 상속받은 자식클래스 혹은 자식클래스와 비슷한 서브 기능들의 정보가 알아서 전부 변경될 경우이다.
  • OCP : 개방-폐쇄 원칙(Open/closed principle)

    • 개방, 폐쇄 원칙은 "소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다" 라고 명시되어있다 이렇게 읽어보면 한번에 이해하기 쉽지 않은데 다형성을 생각해보면 쉽게 이해할 수 있다.

    • public class TempService{
        ParentRepository temp = new TempRepository();
        ParentRepository temp = new TempRepository2();
      
      }
    • 변경이 없다는 것은 코드의 변경이 없다는 말이고, 다형성을 사용하고 있으나 OCP가 지켜지지 않고 있다.

    • 클라이언트는 역할만 보고 구현체가 변경되더라도 상관이 없다 위 코드는 ParentRepository는 동일하지만 할당되는 객체는 변경해서 사용하고 있다.

  • LSP : 리스코프 치환 원칙 (Liskov substitution principle)

    • "프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스를 바꿀 수 있어야 한다." 여기서 말하는 정확성이란 한 가지 기능에 대한 의미를 뜻한다 예를들어 상위 인터페이스의 메소드 중 jump라는 기능을 상속받은 하위클래스 2개가 인스턴스를 생성했다고하자(2개의 하위클래스는 각각 다른 도메인이다) 다람쥐, 캥거루 라는 2개의 하위인스턴스는 jump라는 함수를 실행했는데 여기서 캥거루는 일반적인 jump의 기능을 했지만 다람쥐는 jump를 실행했음에도 바닥을 기어가는 결과가 나온다면 리스코프 치환 원칙의 정확성이 깨지기 때문에 LSP원칙 위반이다.
  • ISP : 인터페이스 분리 원칙 (Interface segregation principle)

    • “특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.” 여기서 자동차 인터페이스를 운전 인터페이스와 정비 인터페이스로 분리했다고 가정하자. 그리고 사용자클라이언트는 운전자 클라이언트와 정비사 클라이언트로 분리한다. 만약 정비 인터페이스에서 수정이 일어났다고 하면 운전자 클라이언트에는 영향이 없겠으나, 범용적인 자동차 인터페이스로 사용하고 있었다면 이를 사용하던 운전자, 정비사 클라이언트 모두 수정이 필요했을 것이다. 예시를 통해 인터페이스가 특정 클라이언트를 위해 생성된다면 더 명확해지며 대체가능성이 높아진다.
  • DIP : 의존관계 역전 원칙 (Dependency inversion principle)

    • 프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.” 의존성 주입은 이 원칙을 따르는 방법 중 하나다. 즉 구현클래스에 의존하지 말고 인터페이스에 의존하라는 뜻이다

Reference

'Spring, Spring Boot' 카테고리의 다른 글

Spring Web Layer  (0) 2023.01.12
Spring MVC 동작과정  (0) 2023.01.11
Spring AOP 정리  (0) 2022.01.01
Spring 어노테이션(Annotation) 정리  (0) 2021.12.28

완전탐색이란?

  • 완전탐색은 Brute-force Search라고 부르며 알고리즘 문제를 해결할 때 기본적으로 처음 접할 수 있는 방법입니다.
  • 한 상황을 생각해 봅시다. 1000개까지 담길 수 있는 배열에서 두개의 가장 큰 수를 찾아 두 수의 합을 출력해보는 문제가 있다고 한다면, 이 두 수를 어떻게 찾을 수 있을까요?
  • 쉽게 생각해서 우리는 for문을 활용해 각 배열의 원소들을 더해보고 가장 큰 값을 구하는 방법을 생각해 볼 수 있습니다.
  • 즉 쉽게 말하면 모든 경우의 수를 다 탐색해보며 찾아보는 방식이고 그렇기 때문에 시간복잡도는 최대로 O(n^2) 의 시간이 소요됩니다.
  • #include <iostream>
    #include <algorithm>
    #include <climits>
    using namespace std;
    int Number;
    int ret = INT_MIN;
    int main()
    {
      int array[1000];
      cin >> Number; // Array의 크기를 입력받습니다.
      for(int i=0;i<Number;i++) // Number의 크기만큼 순회하며 Array의 요소를 채워넣습니다.
        cin >> array[i];
      //여기서 array담긴 숫자들 중 가장 큰 값을 가지는 숫자 2개를 더한 값을 호출해야하는 경우를 생각해봅시다.
      for(int i=0;i<Number;i++)
          for(int j=0;j<Number;j++)
          if (i!=j) // Array의 두 index가 다를 때, 즉 같은숫자의 경우는 제외합니다.
            ret = max(ret, array[i] + array[j]); //return값을 max함수를 통해 더 큰값으로 갱신합니다.
      cout << ret;
      return (0);
    }
  • 위 코드를 살펴보면 Array의 담겨있는 최대 1000개까지 담길 수 있는 배열에서 2개의 큰 수를 찾아 결과를 반환하는 것을 확인할 수 있습니다.
  • 모든 가능한 경우를 다 돌아보기 때문에 Number크기(Array의 크기)의 제곱만큼의 시간이 소요될 수 있습니다.
  • 정답을 찾아내는 방법 중 가장 확실한 방법이지만 그만큼 시간이 많이 소요되기 때문에 알고리즘문제의 제한시간 을 확인해서 사용해야 합니다.
  • c++기준으로 1억번의 시간복잡도를 가질 경우 제한시간이 1초 소요되는 것으로 생각하여 문제를 해결합시다.
  • 완전탐색 알고리즘을 활용한 문제는 다음 게시글에서 확인할 수 있습니다.

+ Recent posts