React.js, Spring 등 라이브러리나 프레임워크의 기본이 되는 디자인 패턴

어떠한 방식으로 로직을 구성해야 하는지에 대한 시각이 담겨 있는 프로그래밍 패러다임

 

용어 정리 - 라이브러리와 프레임워크

용어 : 라이브러리란? - 미리 만들어진 함수나 코드 모음

공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것을 의미한다.

폴더명, 파일명 등에 대한 규칙이 없고, 프레임워크에 비해 자유롭다.

'내가' 직접 컨트롤하여 사용하는 도구 '가위'와 비슷하다. 도구는 각각의 용도가 있다.

 

용어 : 프레임워크란? - 소프트웨어 개발 등 특정 목적을 달성하기 위해 재사용 가능한 기본 구조나 설계, 규칙 등을 미리 제공하여 개발을 더 쉽고 빠르게 할 수 있도록 돕는 도구 or 체계

공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것을 의미한다.

폴더명, 파일명 들에 대한 규칙이 있고, 라이브러리에 비해 좀 더 엄격하다.

'내가' 의존해서 사용하는 도구 '비행기'와 비슷하다.

나는 '비행기'가 정해놓은 규칙을 따르고, '비행기'가 복잡한 작업들을 내부적으로 컨트롤한다.


주요 의미
- 뼈대/골조: 건물을 지을 때의 뼈대처럼, 앱이나 웹사이트의 기본 구조를 잡아주는 역할. 
- 틀/체계/구조: 정해진 규칙과 도구를 제공하여 개발자가 일정한 방식(틀)으로 작업하게 함. 
- 공통 기능 제공: 데이터베이스 연결, 화면 구현 등 반복되는 공통 기능을 미리 만들어 제공해 개발 시간과 비용 절감

 

간단한 비유
- 프레임워크: 틀이 짜여진 집, 사용자는 가구(코드)만 채우면 됨. 
- 라이브러리(비교): 틀을 직접 짜고 필요한 부품(라이브러리)만 가져다 쓰는 것. 
예시: 웹 프레임워크(Django, Spring)는 웹사이트 개발의 기본 구조를 제공하고, 게임 프레임워크는 렌더링이나 파일 입출력 모듈을 제공하는 식입니다

 

Section 1.1 디자인 패턴

용어 : 디자인 패턴이란? - 소프트웨어 개발 시 자주 발생하는 문제에 대한 검증된 해결책으로, 코드의 재사용성과 유연성을 높여주는 '설계 방식의 표준화된 틀', '설계 기법', '설계 모범 사례'

프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것을 의미한다.

주요 개념
- 정의: 반복되는 디자인 문제에 대한 검증된 해결책을 모아놓은 청사진 (Blueprint).
- 목표: 유연하고 효율적으로 객체와 클래스를 조립하는 방법 제시, 코드 품질 향상. 

주요 분류 (GoF 기준)
- 생성 패턴 (Creational Patterns): 객체 생성 과정을 캡슐화하여 유연하게 만듦 (예: 싱글턴, 팩토리 메서드, 빌더).
- 구조 패턴 (Structural Patterns): 클래스와 객체를 조합하여 더 큰 구조를 만듦 (예: 어댑터, 데코레이터, 프록시).
- 행동 패턴 (Behavioral Patterns): 객체 간의 상호작용과 책임 분담에 초점 (예: 옵서버, 전략, 커맨드). 

왜 사용하는가?
- 코드 가독성 및 유지보수성 향상: 검증된 구조로 인해 코드 이해가 쉬워짐.
- 생산성 증대: 문제 해결을 위한 고민 시간을 줄이고, 재사용 가능한 솔루션 적용.
- 객체지향 설계 능력 향상: 클래스와 객체에 대한 시야를 넓혀줌. 

 

1.1.1 싱글톤 패턴

용어 : 싱글톤 패턴이란? - 특정 클래스의 인스턴스가 애플리케이션 전체에서 오직 하나만 생성되도록 보장하는 생성 디자인 패턴으로, 단 하나의 객체를 공유하며 전역 접근을 가능하게 하여 리소스 관리나 데이터 공유에 유용하지만, 테스트 어려움, 결합도 증가 등의 단점도 존재

하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴

하나의 클래스를 기반으로 여러 개의 개별적인 인스턴스를 만들 수 있지만, 그렇게 하지 않고 하나의 클래스를 기반으로  단 하나의 인스턴스를 만들어 이를 기반으로 로직을 만드는 데 쓰이며, 보통 데이터베이스 연결 모듈에 많이 사용

장점
- 메모리 절약: 객체를 여러 번 생성하지 않아 메모리 사용을 줄입니다.
- 데이터 공유: 모든 곳에서 동일한 객체를 통해 데이터를 공유하고 관리하기 쉽습니다 (예: 데이터베이스 연결 풀, 설정 관리).
- 엄격한 제어: 전역 변수 사용을 통제하고 하나의 인스턴스만 사용하도록 보장합니다. 

단점
- 테스트 어려움: 전역 상태를 공유하므로 단위 테스트(Unit Test)가 어렵고 Mock 객체로 대체하기 힘듭니다.
- 높은 결합도: 여러 클래스가 싱글톤 객체에 의존하게 되어 코드 간의 결합도(Coupling)가 높아질 수 있습니다.
- 책임 과중: 단일 책임 원칙(SRP)을 위반하고, 싱글톤 인스턴스 자체의 관리 책임이 발생합니다. 

사용 예시
- 로그 기록(Logger): 시스템 전체에서 하나의 로그 기록 객체를 공유.
- 데이터베이스 연결 풀(DBCP): 여러 곳에서 데이터베이스 연결을 효율적으로 공유.
- 설정 관리(Configuration Manager): 애플리케이션 설정을 한 곳에서 관리

 

자바스크립트의 싱글톤 패턴

// javascript에서 const 변수 = {}는 Object라는 내부 클래스로 인스턴스를 만드는 것과 동일하다.
const obj1 = { a: 27 } // 또는 new Object({a: 27})
const obj2 = { b: 27 }
console.log(obj1 === obj2) // false

// 일반적인 Singleton 패턴

class Singleton {
  constructor() {
    if (!Singleton.instance) {
    	Singleton.instance = this
    }
    return Singleton.instance
  }
  
  // 보통 아래와 같은 함수를 정의
  getInstance() {
  	return this
  }
}

const a = new Singleton()
const b = new Singleton()
console.log(a === b) // true

// DB 연결 Singleton 패턴

class DB {
  constructor(url) {
    if (!DB.instance) {
      DB.instance = createConnection(url)
    }
  }
  
  getInstance() {
  	return this.instance
  }
}

 

Java에서의 싱글톤 패턴

class Singleton {
  // private로 생성자를 감춤
  private static class singleInstanceHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  
  public static Singleton getInstance() {
    return singleInstanceHolder.INSTANCE;
  }
}

public class HelloWorld {
  public static void main(String[] args) {
    Singleton a = Singleton.getInstance();
    Singleton b = Singleton.getInstance();
    System.out.println(a.hashCode() == b.hashCode()); // true
  }
}

 

Mongoose의 싱글톤 패턴

싱글톤 패턴은 Node.js에서 MongoDB 데이터베이스를 연결할 때 쓰는 mongoose 모듈에서 볼 수 있다.

mongoose의 데이터베이스를 연결할 때 쓰는 connect()라는 함수는 싱글톤 인스턴스를 반환한다.

Mongoose.prototype.connect = function(uri, options, callback) {
  const _mongoose = this instanceof Mongoose ? this : mongoose;
  const conn = _mongoose.connection;
  return _mongoose._promiseOrCallback(callback, cb => {
  	conn.openUri(uri, options, err => {
    	if (err != null) {
        	return cb(err);
        }
        return cb(null, _mongoose);
    }
  });
}

 

MySQL의 싱글톤 패턴

Node.js에서 MySQL 데이터베이스를 연결할 때도 싱글톤 패턴이 쓰입니다.

// Main Module
const mysql = require('mysql');
const pool = mysql.createPool({
	connectionLimit: 10,
    host: 'example.org',
    user: 'user',
    password: 'password',
    database: 'db'
});
pool.connect();

// A Module
pool.query(query, ...);

// B Module
pool.query(query, ...);

 

싱글톤 패턴의 단점

TDD(Test Driven Development)를 할 때 걸림돌이 된다.

TDD를 할 때 단위 테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며, 테스트를 어떤 순서로든 실행할 수 있어야 합니다.

싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 '독립적인' 인스턴스를 만들기가 어렵습니다.

 

의존성 주입

용어: 의존성이란? - 종속성이라고도 하며 A가 B에 의존성 있다는 것은 B의 변경 사항에 대해 A 또한 변경해야 된다는 것을 의미합니다.

용어 : 의존성 주입(Dependency Injection)이란? - 객체가 필요로 하는 다른 객체를 직접 생성하는 대신외부에서 생성하여 주입해주는 디자인 패턴으로, 코드의 결합도를 낮춰 유연성과 유지보수성을 높이며 테스트하기 쉽게 만드는 핵심 기술, 이는 객체 간의 의존 관계를 외부에서 설정하거나 프레임워크(스프링 등)가 대신 관리해주어, 특정 객체의 변경이 다른 객체에 미치는 영향을 최소화합니다. 

 

싱글톤 패턴은 사용하기가 쉽고, 굉장히 실용적이지만 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있습니다.

이때 의존성 주입을 통해 모듈 간의 결합을 조금 더 느슨하게 만들어 해결할 수 있습니다.

주요 개념
- 의존성(Dependency): A 객체가 B 객체를 사용할 때, A는 B에 의존한다고 하며 서로 영향을 주는 관계를 의미합니다.
- 주입(Injection): 의존하는 객체(의존성)를 외부에서 생성하여 주입해주는 행위입니다. 

 

장점
- 결합도 낮춤: 객체 간의 결합도를 낮춰 유연성을 확보합니다.
- 유지보수성 향상: 변경에 대한 영향을 줄여 유지보수가 용이해집니다.
- 테스트 용이: 외부에서 의존성을 주입하므로, 테스트 시 가짜 객체(Mock)를 쉽게 주입하여 테스트할 수 있습니다.
- 재사용성 증가: 코드의 종속성이 줄어 재사용성이 높아집니다

 

주입 방식 (스프링 기준)
- 생성자 주입 (Constructor Injection): 생성자를 통해 의존성을 주입하며 불변성이 보장되어 가장 권장됩니다.
- 메서드 주입 (Method Injection): setter와 같은 메서드를 통해 주입합니다.
- 필드 주입 (Field Injection): 필드에 직접 주입하며 코드가 간결하지만 테스트가 어렵고 프레임워크에 종속적이라 비추천됩니다. 

 

예시
- Store 클래스가 Pencil 객체를 사용한다고 할 때, Store 내부에서 Pencil을 직접 생성하는 대신, 외부에서 Pencil 인스턴스를 만들어 Store에 넣어주는 방식이 의존성 주입입니다. 스프링에서는 이러한 과정을 컨테이너가 자동으로 처리해줍니다.

 

 

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기