디자인 패턴 : 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계등을 이용하여 해결할 수 있도록 하나의 '규약'형태로 만들어 놓은 것
1) 라이브러리 : 공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것
👉 폴더명, 파일명 등에 대한 규칙이 없고 프레임워크에 비해 자유롭다.
📍 예시. 무언가를 자를 때 '도구'인 '가위'를 사용해서 '내가' 직접 컨트롤하여 자른다.
2) 프레임워크 : 공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것
👉 폴더명, 파일명 등에 대한 규칙이 있으며 라이브러리에 비해 좀 더 엄격하다.
📍 예시. 다른 곳으로 이동할 때 '도구'인 비행기를 타고 이동하지만 '비행기'가 컨트롤하고 나는 가만히 앉아있어야 한다.
✅ 모듈이란? 모듈은 부품이라고 생각하면 된다. 특정 기능별로 나누어지는 프로그램 덩어리로 재활용과 유지보수에 용이하도록 한다. (참고 : https://doh-an.tistory.com/3)
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에서 메인 모듈에서 데이터베이스 연결에 관한 인스턴스를 정의하고 다른 모듈에서 해당 인스턴스를 기반으로 쿼리를 보내는 형식
: 싱글톤 패턴은 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있는데 이 때 의존성 주입을 통해 모듈간의 결합을 조금 더 느슨하게 만들어 해결할 수 있다.
✅ 의존성 : 종속성이라고도 하며, 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 전공지식 노트