- π Tight-coupling & Dependency
- π Loose-coupling & Dependency
- π μ±κΈν€ ν¨ν΄
- π DI(Dependency Injection/ μμ‘΄μ±μ£Όμ )
- π IoC(Inversion of Control / μ μ΄μ μμ )
- π TypeScript μ¬μ© μ΄μ λ° μ¬μ©λ°©λ²
- π Decorator μ리
- π TypeScript μ¬μ© μ΄μ λ° μ¬μ©λ°©λ²
- π μ€λ ν루
κ°ν κ²°ν©(Tight Coupling)μ ν΄λμ€μ κ°μ²΄κ° μλ‘ μμ‘΄(Dependency)νκ³ μλ€λ κ²μ μλ―Ένλ€.
νλμ κ°μ²΄λ₯Ό λ³κ²½νκ² λλ©΄ λ€λ₯Έ κ°μ²΄λ€μ λ³κ²½μ μꡬλκΈ° λλ¬Έμ λ³κ²½μ λ€μ μ½κ² λμΉ μ μλ€.
κ²°ν©μ΄ κ°νκ² λμ΄μμ΄ κ²°ν©μ΄ λμ΄μμ§ μμΌλ©΄ μ¬μ© ν μ μλ€.
newλ₯Ό μ μΈν λλ§λ€ μ»΄ν¨ν° λ©λͺ¨λ¦¬λ₯Ό μ¬μ©νκ² λλ€. λ°λΌμ λΉκ΅μ κ°ν κ²°ν©μμ newλ₯Ό λ λ§μ΄ μ¬μ©νκΈ° λλ¬Έμ λ©λͺ¨λ¦¬λ₯Ό λ§μ΄ μ‘μλ¨Ήκ² λλ€.
// product.controller.js import { CashService } from "./services/cash.service.js"; import { ProductService } from "./services/product.service.js"; export class ProductController { buyProduct = (req, res) => { // 1. κ°μ§λ κ²μ¦νλ μ½λ (λλ΅ 10μ€ => 2μ€) const cashService = new CashService(); const hasMoney = cashService.checkValue(); // true λλ false λ¦¬ν΄ // 2. ν맀μ¬λΆ κ²μ¦νλ μ½λ (λλ΅ 10μ€ => 2μ€) const productService = new ProductService(); const isSoldout = productService.checkSoldout(); // true λλ false λ¦¬ν΄ // 3. μν ꡬ맀νλ μ½λ if (hasMoney && !isSoldout) { res.send("μν ꡬ맀 μλ£!!"); } }; refundProduct = (req, res) => { // 1. ν맀μ¬λΆ κ²μ¦νλ μ½λ (λλ΅ 10μ€ μ λ) const productService = new ProductService(); const isSoldout = productService.checkSoldout(); // true λλ false λ¦¬ν΄ // 2. μν νλΆνλ μ½λ if (isSoldout) { res.send("μν νλΆ μλ£!!"); } }; }
μμ μμ€μ½λμμ ProductControllerκ° CashServiceμ ProductServiceμ μμ‘΄νκ³ μλ μνμ΄λ€.
λ€λ₯Έ λ§λ‘λ κ°νκ² κ²°ν©λμ΄ μλ€κ³ λΆλ₯΄κΈ°λ νλ€.
λμ¨ν κ²°ν©μ κ°ν κ²°ν©μ λ°λ κ°λ μ΄λ©°, λ€λ₯Έ ν΄λμ€λ₯Ό μ§μ μ μΌλ‘ μ¬μ©νλ ν΄λμ€μ μμ‘΄μ±μ μ€μ΄λ κ²μ΄λ€.
ν΄λμ€/ν΄λμ€λ₯Ό λμ¨νκ² κ²°ν©λμ΄ μλ‘μ΄ κΈ°λ₯μ κ°λ°νκ±°λ κΈ°μ‘΄ κΈ°λ₯μ μμ νκ³ νμ₯νλκ² μ½λ€.
μ½λμ μ μ§ λ³΄μκ° μ½λ€.
ν μ€νΈ λμμΌλ‘ μΉννκΈ°κ° μ¬μ μ λ ν μ€νΈκ° μ©μ΄νλ€.
// index.js // const express = require('express') // μλ λ°©μ => commonjs import express from "express"; // μμ¦λ°©μ => module import { ProductController } from "./mvc/controllers/product.controller.js"; import { CouponController } from "./mvc/controllers/coupon.controller.js"; import { ProductService } from "./mvc/controllers/services/product.service.js"; const app = express(); // μΆκ°λλ λΆλΆ const cashService = new CashService(); // μν API const productController = new ProductController(cashService); app.post("/products/buy", productController.buyProduct); app.post("/products/refund", productController.refundProduct); // μΏ ν°(μνκΆ) API const couponController = new CouponController(); app.post("/coupons/buy", couponController.buyCoupon); app.listen(3000, () => { console.log("λ°±μλ API μλ²κ° μΌμ‘μ΄μ!!!"); });
product.controller.js
νμΌμ μλ new CashService()λ₯Ό λ°μμ μ€νμμΌμ€ κ²μΈλ°, index.js
νμΌμμ μ€νμμΌ μ€ κ²μ΄λ€.
μΆκ° μμ±ν cashServiceλ₯Ό ProductController() μμ λ£μ΄μ€μΌλ‘μ¨ λ°μμ λ§λ€μ΄μ μμΌλ‘ λ£μ΄μ£Όκ² λλ κ²μ΄λ€.
μ¦, cashServiceλ₯Ό μμ±μ
λ‘ λ£μ΄μ€ κ²μ΄λ€. μ΄λ κ² ν¨μΌλ‘μ¨ κ°νκ²°ν©μ μ½νκ²°ν©μΌλ‘ λ°κΏ μ μλ€.
λν, μ΄λ¬ν λ°©λ²μ μ±κΈν€ ν¨ν΄μ΄λ€.
λΉμ¦λμ€ λ‘μ§μΈ CashService
λ₯Ό λ¨Όμ μ μΈνμλ€.
μ΄λ κ² νλ©΄ new ν λ²μΌλ‘ λͺ¨λ κ³³μμ μ¬μ© κ°λ₯νκ² λλ€.
μ΄λ° λμμΈ ν¨ν΄μ Singleton Pattern(μ±κΈν€ ν¨ν΄)μ΄λΌκ³ νλ€.
μ§κΈκΉμ§ μ΄ν΄λ³Έ λ°μ κ°μ΄ μμ‘΄μ±μ£Όμ μΌλ‘ μ»μ μ μλ μ±κΈν€ν¨ν΄μ μ₯μ μΌλ‘λ 2κ°μ§κ° μ‘΄μ¬νλ€.
new ν λ²μΌλ‘ λͺ¨λ κ³³μμ μ¬μ© κ°λ₯νλ€.
κ°νκ² κ²°ν©λμ΄ μλ μνμμ λμ¨ν κ²°ν©μΌλ‘ νλ¦¬κ² λλ€.
DI(Dependency Injection) μμ‘΄μ±μ£Όμ μ Tight Coupling(κ°ν κ²°ν©)μ Loose Coupling(λμ¨ν κ²°ν©)μΌλ‘ μ ν μν€λ λ°©λ²μ΄λ©°, μ μ΄μ μμ (Inversion of Control)μ κΈ°μ μ€ νλμ΄λ€.
μ μ΄μ μμ :Β "λ΄κ° λμ μ μ΄ν΄ μ€κ²"
μμ‘΄μ±μ£Όμ :Β "λκ° μ μν μ½λ(ν΄λμ€, λ³μ λ±λ±)λ₯Ό"
μμ‘΄μ±μ£Όμ μ΄λ©΄ μ±κΈν€ν¨ν΄μ΄ μλλ€.
new λ₯Ό λ°μΌλ‘ λΉΌμ£Όκ² λλ©΄μ μ±κΈν€ν¨ν΄μ΄ λ κ²μ λ§μ§λ§ μμ‘΄μ±μ£Όμ μ΄λΌλ κ²μ λ°μμ μμΌλ‘ λ£μ΄μ£Όλ κ²μ΄κΈ°μ κ°λ€κ³ λ ν μ μλ€.
μμ‘΄μ±μ£Όμ μΌλ‘ μ»μ μ μλ μ₯μ 2κ°μ§μ κ³Όμ μ΄ NestJS μμλ μλμΌλ‘ μ΄λ£¨μ΄μ§κ² λλ€!
λ°λΌμ, new λ₯Ό μ¬μ©νμ¬ μ°λ¦¬κ° λ§λ€μ΄μ€μΌνλ κ²λ€, μμ‘΄μ±μ£Όμ
λ±μ NestJSκ° ν΄μ£Όκ² λλ©΄μ NestJS μͺ½μΌλ‘ μ μ΄κ° μμ (IoC)
λμλ€.
μ μ΄μ μμ (Inversion of Control)
μ μΌλ°μ μΈΒ λμμΈ ν¨ν΄Β μ€ νλλ€.
μΌλ°μ μΌλ‘ κ°λ°μκ° νλ‘κ·Έλ¨μ νλ¦μ μ μ΄νλ 주체μλ€λ©΄, IoCμ κ°λ μ΄ λμ€κ² λλ©΄μ νλ μμν¬κ° dependencyλ₯Ό containerν μμΌ μλͺ μ£ΌκΈ°λ₯Ό κ΄λ¦¬νκ² λμλ€.
μ¦, dependencyμ μ μ΄κΆμ΄ κ°λ°μμμ νλ μμν¬λ‘ λμ΄κ°κ² λμμΌλ©° μ΄λ₯Ό μ μ΄κΆμ νλ¦μ΄ λ³κ²½λμλ€κ³ νμ¬ IoC(Inversion of Control)
λΌκ³ νλ€.
νμ μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈμ νμ μ λΆμ¬ν μΈμ΄μ΄λ€.
μλ°μ€ν¬λ¦½νΈμ νμ₯λ μΈμ΄λΌκ³ λ³Ό μ μλ€.
νμ μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈμ λ¬λ¦¬ λΈλΌμ°μ μμ μ€ννλ €λ©΄ νμΌμ νλ² λ³νν΄ μ£Όμ΄μΌ νλ€.
μ΄ λ³ν κ³Όμ μΒ μ»΄νμΌ(complile)Β μ΄λΌκ³ λΆλ₯Έλ€.
μλ°μ€ν¬λ¦½νΈλ μΆ©λΆν 볡μ‘νκ³ μ΄λ €μ΄λ° μ λ λ€λ₯Έ μΈμ΄λ₯Ό λ°°μμΌ ν κΉ? λ¨μ§ μ΅μ κΈ°μ μ΄λΌμ? νΉμ λ€λ₯Έ νμ¬λ λ§μ΄ μ¬μ©νλκΉ μ°λ¦¬λ μ¨μΌ νλ κ±ΈκΉ? λΌλ κ³ λ―Όμ ν μ μλ€.
νμ μ€ν¬λ¦½νΈλ μλ 2κ°μ§ κ΄μ μμ μλ°μ€ν¬λ¦½νΈ μ½λμ νμ§κ³Ό κ°λ° μμ°μ±μ λμΌ μ μλ€.
μλ¬μ μ¬μ λ°©μ§
μ½λ κ°μ΄λ λ° μλ μμ±(κ°λ° μμ°μ± ν₯μ)
//* νμ μΆλ‘ let aaa = "μλ νμΈμ"; //μ²μ λ€μ΄μ¨ κ°μΌλ‘ νμ μ μΆλ‘ νλ€ // :νμ -> κ΅³μ΄ μν΄λ λλ€ // aaa = 3; (X) //* νμ λͺ μ let bbb: string = "λ°κ°μ΅λλ€"; // bbb = 10; (X) //* νμ λͺ μκ° νμν μν© // number λλ string νμ let ccc: number | string = 1000; ccc = "1000μ"; //* μ«μνμ let ddd1 = 10; let ddd2: number = 10; // ddd1 = "μ² μ" (X) //* λΆλ¦°νμ let eee: boolean = true; eee = false; // eee = "false" ("false" -> trueλ‘ μλνλ€.) //* λ°°μ΄νμ let fff: number[] = [1, 2, 3, 4, 5]; let ggg: string[] = ["μ² μ", "μν¬", "νμ΄"]; let hhh = ["μ² μ", "μν¬", "νμ΄", 1]; // νμ μ μΆλ‘ ν΄μ μ΄λ€ νμ μ μ¬μ©νλμ§ μμ보기!! //* κ°μ²΄νμ interface IProfile { name: string; age: number | string; school: string; hobby?: string; // hobby? -> μμ΄λλκ³ μμ΄λλ¨ } const profile: IProfile = { name: "μ² μ", age: 8, school: "λ€λμ₯μ΄λ±νκ΅", }; profile.name = "νμ΄"; // νμ μΆλ‘ μΌλ‘λ μ΄κ²λ§ κ°λ₯!! profile.age = "8μ΄"; profile.hobby = "μμ"; //* anyνμ let qqq: any = "μ² μ"; //μλ°μ€ν¬λ¦½νΈμ λμΌ! qqq = 123; qqq = true; //* ν¨μνμ => μ΄λμ λκ° μ΄λ»κ² νΈμΆν μ§ λͺ¨λ₯΄λ―λ‘, νμ μΆλ‘ ν μ μμ (λ°λμ, νμ λͺ μ νμ!!) function add(num1: number, num2: number, unit: string): string { // λμ :string -> return νμ return num1 + num2 + unit; } const result = add(1000, 2000, "μ"); // κ²°κ³Όμ λ¦¬ν΄ νμ λ μμΈ‘ κ°λ₯!!! //* νμ΄ν ν¨μ const add2 = (num1: number, num2: number, unit: string): string => { // λμ :string -> return νμ return num1 + num2 + unit; }; const result2 = add2(1000, 2000, "μ"); // κ²°κ³Όμ λ¦¬ν΄ νμ λ μμΈ‘ κ°λ₯!!!
λ°μ½λ μ΄ν°λ μ€ννλ €λ μ¬μ©μκ° κ΅¬μ‘°λ₯Ό μμ νμ§ μκ³ κΈ°μ‘΄ κ°μ²΄μ μλ‘μ΄ κΈ°λ₯μ μΆκ°ν μ μλλ‘ νλ λμμΈ ν¨ν΄μ΄λ€.
μΌλ°μ μΌλ‘ λ°μ½λ μ΄νΈ νλ €λ ν¨μμ μ μ μ μ νΈμΆλλ€.
λ°μ½λ μ΄ν°λ ν¨μλ₯Ό μΈμλ‘ μ»κ³ λκ°λ‘ μλ‘μ΄ ν¨μλ‘ λλ €μ£Όλ callable(μ λ¬λ°μ object μΈμκ° νΈμΆ κ°λ₯νμ§λ₯Ό νλ¨)ꡬ쑰 μ΄λ€.
μλ°μ€ν¬λ¦½νΈλ₯Ό νμ₯ν μΈμ΄λΌκ³ ν μ μλ νμ μ€ν¬λ¦½νΈμμλ μ€νμ μΈ κΈ°λ₯μΌλ‘ λ°μ½λ μ΄ν°λ₯Ό μ 곡νκ³ μλ€.
λ°λΌμ 컀맨λ λΌμΈμ΄λ tsconfig.json
μμ experimentalDecorators
μ΅μ
μ μΆκ°ν΄ μ€μΌ νλ€.
class Monster2 { // power; => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ constructor(public power) { // this.power = power; // => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ } attack1 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ μ κ·Ό κ°λ₯ this.power = 30; // μμμ λ³κ²½ κ°λ₯ }; } class 곡μ€λͺ¬μ€ν°2 extends Monster2 { attack2 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ΄ μ κ·Ό κ°λ₯ this.power = 30; // μμμ΄ λ³κ²½ κ°λ₯ }; } const myMonster22 = new 곡μ€λͺ¬μ€ν°2(20); myMonster22.attack1(); myMonster22.attack2(); console.log(myMonster22.power); // μΈλΆμμ μ κ·Ό κ°λ₯ myMonster22.power = 50; // μΈλΆμμ λ³κ²½ κ°λ₯
class Monster2 { // power; => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ constructor(private power) { // this.power = power; // => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ } attack1 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ μ κ·Ό κ°λ₯ this.power = 30; // μμμ λ³κ²½ κ°λ₯ }; } class 곡μ€λͺ¬μ€ν°2 extends Monster2 { attack2 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ΄ μ κ·Ό λΆκ° this.power = 30; // μμμ΄ λ³κ²½ λΆκ° }; } const myMonster22 = new 곡μ€λͺ¬μ€ν°2(20); myMonster22.attack1(); myMonster22.attack2(); console.log(myMonster22.power); // μΈλΆμμ μ κ·Ό λΆκ° myMonster22.power = 50; // μΈλΆμμ λ³κ²½ λΆκ°
class Monster2 { // power; => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ constructor(protected power) { // this.power = power; // => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ } attack1 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ μ κ·Ό κ°λ₯ this.power = 30; // μμμ λ³κ²½ κ°λ₯ }; } class 곡μ€λͺ¬μ€ν°2 extends Monster2 { attack2 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ΄ μ κ·Ό κ°λ₯ this.power = 30; // μμμ΄ λ³κ²½ κ°λ₯ }; } const myMonster22 = new 곡μ€λͺ¬μ€ν°2(20); myMonster22.attack1(); myMonster22.attack2(); console.log(myMonster22.power); // μΈλΆμμ μ κ·Ό λΆκ° myMonster22.power = 50; // μΈλΆμμ λ³κ²½ λΆκ°
class Monster2 { // power; => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ constructor(readonly power) { // this.power = power; // => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ } attack1 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ μ κ·Ό κ°λ₯ this.power = 30; // μμμ λ³κ²½ λΆκ° }; } class 곡μ€λͺ¬μ€ν°2 extends Monster2 { attack2 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ΄ μ κ·Ό κ°λ₯ this.power = 30; // μμμ΄ λ³κ²½ λΆκ° }; } const myMonster22 = new 곡μ€λͺ¬μ€ν°2(20); myMonster22.attack1(); myMonster22.attack2(); console.log(myMonster22.power); // μΈλΆμμ μ κ·Ό κ°λ₯ myMonster22.power = 50; // μΈλΆμμ λ³κ²½ λΆκ°
class Monster2 { // power; => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ constructor(private readonly power) { // this.power = power; // => public, private, protected, readonly μ€ 1κ°λΌλ μμΌλ©΄ μλ΅ κ°λ₯ } attack1 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ μ κ·Ό κ°λ₯ this.power = 30; // μμμ λ³κ²½ λΆκ° }; } class 곡μ€λͺ¬μ€ν°2 extends Monster2 { attack2 = () => { console.log("곡격νμ!!"); console.log("λ΄ κ³΅κ²©λ ₯μ" + this.power + "μΌ!!!"); // μμμ΄ μ κ·Ό λΆκ° this.power = 30; // μμμ΄ λ³κ²½ λΆκ° }; } const myMonster22 = new 곡μ€λͺ¬μ€ν°2(20); myMonster22.attack1(); myMonster22.attack2(); console.log(myMonster22.power); // μΈλΆμμ μ κ·Ό λΆκ° myMonster22.power = 50; // μΈλΆμμ λ³κ²½ λΆκ°
μ€λμ νμ μ€ν¬λ¦½νΈμ λνμ¬ νμ΅ν λ μ΄λ€. νμ μ€ν¬λ¦½νΈλ₯Ό ν΅ν΄ κΈ°μ‘΄ μλ°μ€ν¬λ¦½νΈλ³΄λ€ λͺ ννκ² μλ£νμ μ μΈνκ³ λ°μ΄ν°λ₯Ό λ€λ£° μ μκ² λλ©΄μ μμΌλ‘ νμ΅ν λ€μ€νΈjsμ λν΄ μ€λΉνλ λ μ΄μλ€.