본문 바로가기
Javascript/Node.js

Adaptor Pattern - 구조적 설계 패턴

by v8rus 2022. 2. 9.

어댑터 (Adaptor)

"다른 인터페이스와 호환가능하도록 인터페이스를 변환"

어댑터의 작업이 다른 객체에 대한 하나 이상의 함수 호출로 구성
일반적으로 "컴포지션"을 사용
다른 객체의 함수로 연결을 제공

 

1. 파일 시스템 API 로 LevelUP 사용하기

LevelUp API 중심으로 어댑터를 만들어 기본 fs 모듈과 호환되는 인터페이스로 변환
readFile(), writeFile() 호출을 db.get(), db.put() 으로 변환시킬거임
일단, 팩토리를 export 하는것으로 시작하겠습니다.

 

import {resolve} from 'path';

export function createFSAdapter (db) {
  return ({
    readFile (filename, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = {};
      } else if (typeof options === 'string') {
        options = { encoding : options}
      }

      // resolve: 전체 경로를 key로 사용
      db.get(resolve(filename), {
        valueEncoding : options.encoding;
      },
      (err, value) => {
        // key 없는경우 에러 처리, 가장 일반적인 오류만 처리함
        if (err) {
          if (err.type === 'NotFoundError') {
            err = new Error(`ENOENT, open "${filename}"`);
            err.code = 'ENOENT';
            err.errno = 34;
            err.path = filename;
          }

          return callback && callback(err);
        }

        // 성공시 콜백을 사용해 호출자에게 값 전송
        callback && callback(null, value);
      })
    },
    writeFile (filename, contents, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = {};
      } else if (typeof options === 'string') {
        options = { encoding : options };
      }

      db.put(resolve(filename), {
        valueEncoding : options.encoding,
      }, callback);
    }
  })
}

여기서 작성된 코드들은 완벽한 래퍼가 아닙니다....

파일 권한과 같이 일부 옵션을 무시하고 DB 오류를 받은 그대로 전달합니다 ㅋㅋ

 

// index.js - 테스트 모듈
import fs from 'fs';

fs.writeFile('file.txt', 'Hello!', () => {
  fs.readFile('file.txt', { encoding : 'utf8', }, (err,res) => {
    if (err) {
      return console.log(err);
    }
    console.log(res);
  })
})
// 누락된 파일 읽기 시도
fs.readFile('missing.txt', {encoding : 'utf8'}, (err, res) => {
  console.error(err);
})

기존에 이렇게 썼던 것을 어댑터로 변경을 해보면

import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import level from 'level';
import { createFSAdapter } from './fs-adapter.js';

const __dirname = diranme(fileURLToPath(import.meta.url));
const db = level(join(__dirname, 'db'), {
  valueEncoding : 'binary',
})
const fs = createFSAdapter(fs);
// ...

이렇게 쓸 수 있습니다.

직접 지정한 파일 일부를 읽거나 쓰지 않는것을 제외하고 동일한 출력 생성
어댑터를 사용해 수행된 모든 작업은 levelUp DB 에서 수행되는 작업으로 전환

* levelUP 은 DB 를 브라우저에서도 실행 할 수 있는 어댑터가 있음! (levl-js)

 

 

Example Packages

-levelUP 브라우저 사용 가능 이유 : 어댑터 사용
-JugglingDB : 다중 DB ORM 으로 여러 어댑터를 사용해 DB 호환
-nanoSQL : 다양한 dB 지원을 위해 어댑터 패턴을 많이 사용한 DB 추상화 라이브러리
-우리가 만들어 본 것을 보완하고 완벽하게 구현한것은 level-filesystem

댓글