디자인 패턴 개요 및 싱글톤 패턴

JoyJuhee·2022년 7월 5일
0

디자인 패턴

목록 보기
1/5
post-thumbnail

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

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

1) 라이브러리 : 공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것
👉 폴더명, 파일명 등에 대한 규칙이 없고 프레임워크에 비해 자유롭다.

📍 예시. 무언가를 자를 때 '도구'인 '가위'를 사용해서 '내가' 직접 컨트롤하여 자른다.

2) 프레임워크 : 공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것
👉 폴더명, 파일명 등에 대한 규칙이 있으며 라이브러리에 비해 좀 더 엄격하다.

📍 예시. 다른 곳으로 이동할 때 '도구'인 비행기를 타고 이동하지만 '비행기'가 컨트롤하고 나는 가만히 앉아있어야 한다.

모듈이란? 모듈은 부품이라고 생각하면 된다. 특정 기능별로 나누어지는 프로그램 덩어리로 재활용과 유지보수에 용이하도록 한다. (참고 : https://doh-an.tistory.com/3)

2. 싱글톤 패턴(Singleton pattern)

1) 싱글톤 패턴: 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴으로, 보통 데이터베이스 연결 모듈에 많이 사용한다.

2) 장점
◼ 하나의 인스턴스를 만들어 놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용하기 때문에 인스턴스를 생성할 때 드는 비용이 줄어든다.

3) 단점
◼ 의존성이 높아진다.
◼ TDD(Test Driven Development)를 할 때 걸림돌. 단위 테스트를 할때 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.

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

const obj = {
    a: 27
}
const obj2 = {
    a: 27
}
console.log(obj === obj2)
// false -> 하나의 클래스에는 하나의 인스턴스만 가지기 때문에

👉 JS에서는 리터럴 {} 또는 new Object로 객체를 생성하게 되면 다른 어떤 객체와도 같지 않기 때문에 이 자체만으로 싱글톤 패턴을 구현할 수 있다.

📍 데이터베이스 연결 모듈

const URL = 'mongodb://localhost:27017/kundolapp'
const createConnection = url => ({"url" : url})    
class DB {
    constructor(url) {
        if (!DB.instance) { 
            DB.instance = createConnection(url)
        }
        return DB.instance
    }
    connect() {
        return this.instance
    }
}
const a = new DB(URL)
const b = new DB(URL) 
console.log(a === b) // true

👉 DB.instance라는 하나의 인스턴스를 기반으로 a,b를 생성하는 것을 볼 수 있다. 이를 통해 데이터베이스 연결에 관한 인스턴스 생성 비용을 아낄 수 있다.

📍 mongoose의 싱글톤 패턴 : 실제로 싱글톤 패턴은 Node.js에서 MongoDB 데이터베이스를 연결할 때 쓰는 mongoose 모듈에서 볼 수 있다.
📍 MySQL의 싱글톤 패턴 : Node.js에서 메인 모듈에서 데이터베이스 연결에 관한 인스턴스를 정의하고 다른 모듈에서 해당 인스턴스를 기반으로 쿼리를 보내는 형식

3. 의존성 주입(DI, Dependency Injection)

: 싱글톤 패턴은 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있는데 이 때 의존성 주입을 통해 모듈간의 결합을 조금 더 느슨하게 만들어 해결할 수 있다.
✅ 의존성 : 종속성이라고도 하며, a가 b에 의존성이 있다는 것은 b의 변경 사항에 대해 a 또한 변해야 된다는 것을 의미한다.

1) 의존성 주입 : 메인 모듈이 '직접' 다른 하위 모듈에 대한 의존성을 주기보다는, 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 '간접'적으로 의존성을 주입하는 방식
👉 메인 모듈(상위 모듈)은 하위 모듈에 대한 의존성이 떨어지게 된다. (이를 '디커플링이 된다'고도 한다.)

2) 의존성 주입은 컴파일타임이 아닌 런타임에 종속성을 해결하는데 사용된다.

3) 의존성 주입은 프레임워크가 아닌 디자인 패턴이다.
👉 IoC(Inversion of Control)라고 하는 보다 일반적인 소프트웨어 개념을 구현하는 방법 (SOLID 디자인 원칙의 일부)
👉 무언가 쉽게 교체할 수 있게 만든다.

4) 장점

  • 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고, 마이그레이션하기도 수월하다.
  • 구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어주기 때문에 어플리케이션 의존성 방향이 일관되고, 어플리케이션을 쉽게 추론할 수 있으며, 모듈 간의 관계들이 조금 더 명확해진다.

5) 단점

  • 모듈들이 더욱더 분리되므로 클래스 수가 늘어나서 복잡성이 증가될 수 있으며, 약간의 런타임 페널티가 생기기도 한다.
    👉 종속성은 런타임에 해결되기 때문에 종속성 오류는 컴파일 시간에 포착할 수 없다.

✅ 컴파일 vs 런타임
-> 컴파일 : 소스코드를 작성하고 컴파일이라는 과정을 통해 기계어코드로 변환되어 실행가능한 프로그램이 된다. 이러한 편집 과정을 컴파일타임이라고 부른다.
---> 컴파일 에러 : 소스코드가 컴파일되는 과정 중에 발생하는 Syntax error, 파일 참조 오류 등과 같은 문제들로 인해 컴파일이 방해되어 발생하는 오류들. (에러가 발생 시, 현재 문제가 되는 소스코드를 알려준다.)

-> 런타임 : 컴파일과정을 마친 프로그램은 사용자에 의해 실행되어지며, 이러한 응용프로그램이 동작되어지는 떄를 런타임이라고 한다.
---> 런타임 에러 : 이미 컴파일이 완료되어 프로그램이 실행중임에도 불구하고, 의도치 않은 예외 상황으로 인하여 프로그램 실행 중에 발생하는 오류 형태
(참고 : https://velog.io/@yejin20/%EB%9F%B0%ED%83%80%EC%9E%84%EA%B3%BC-%EC%BB%B4%ED%8C%8C%EC%9D%BC%ED%83%80%EC%9E%84-%EC%B0%A8%EC%9D%B4%EC%A0%90)

import axios from "axios";

const fetchResource = (httpClient) => (url) =>
	httpClient(url)
	.then((data) => data.json)
	.catch((error => console.log(error)); // 여기서 의존성 주입 발생

const httpClient = axios.create({
           baseURL : "https://kundol.com",
           method: "GET",
           headers: {
           		"Access-Control-Allow-Origin": "*"
           }
});

const getData = fetchResource(httpClient);
//httpClient라는 서비스를 주입 / fetchResource : 클라이언트
getData("/resourcepath").then((response) => console.log(response))
           

3) 의존성 주입의 원칙

  • 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 한다.
  • 둘 다 추상화에 의존해야 한다.
  • 추상화는 세부 사항에 의존하지 말아야 한다.

출처 : [책] 면접을 위한 CS 전공지식 노트

0개의 댓글