Node.js Express API 서버 설계

임기호·2025년 4월 7일

API 서버 설계

API 서버 설계와 구현에 대해 자세히 공유하려 합니다.

핵심 목표

통합 API 서버는 다음 네 가지 주요 목표를 달성하기 위해 설계되었습니다:

  1. 배포 횟수 절감: DB 프로시저 구조화를 통한 잦은 배포 감소
  2. 다양한 DBMS 연결: PostgreSQL, MySQL, Oracle 등 여러 데이터베이스 연동
  3. 계정 관리 단일화: AD/LDAP 활용한 중앙 집중식 사용자 인증
  4. 유지보수성 강화: 체계적인 코드 구조화를 통한 확장성 확보

1. DB 프로시저 구조화

API 서버의 역할을 단순화하고 DB 프로시저에서 더 많은 핵심 로직을 처리하도록 구조를 변경하여, 잦은 서버 배포 없이 DB 프로시저 수정만으로도 상당 부분의 로직 변경에 유연하게 대처할 수 있도록 구성했습니다.

표준화된 프로시저 반환값 모델

export interface ProcedureResult {
  /** 숫자 값: 0(정상), 양수(유효성 검사 결과), 음수(시스템 오류) */
  numericData: number;
  
  /** 문자열 값: 결과 메시지 */
  textData: string;

  /** 커서 값: 실제 데이터와 메타데이터 */
  cursorData?: Array<{
    data: Record<string, any>[];
    fields: Record<string, any>[];
  }>;
}

주요 이점

  • 프로시저가 validation 및 에러 처리를 전담하여 API 서버 복잡도 감소
  • 데이터 처리 로직 변경 시 프로시저만 수정하면 되어 배포 횟수 대폭 감소
  • 표준화된 응답 형식으로 클라이언트 측 처리 일관성 확보

2. 다양한 DBMS 연동을 위한 팩토리 메서드

파편화된 시스템의 다양한 데이터베이스에 접근하기 위해 팩토리 메서드 패턴을 도입했습니다.

폴더 구조

- connector/
  ├── baseConnector.ts   // 추상 기본 클래스
  ├── postgresConnector.ts
  ├── mysqlConnector.ts
  ├── oracleConnector.ts
  └── index.ts
- executor/
  ├── postgresExecutor.ts
  ├── mysqlExecutor.ts
  ├── oracleExecutor.ts
  └── index.ts

기본 커넥터 구현 (baseConnector.ts)

export abstract class DBConnector {
  static connectors: DBConnector[] = []; // 등록된 커넥터 관리

  constructor() {
    DBConnector.connectors.push(this); // 모든 인스턴스를 중앙에서 관리
  }

  abstract connect(): Promise<void>;
  abstract query<T>(sql: string, params?: any[]): Promise<T>;
  abstract disconnect(): Promise<void>;
  abstract getClient(): Promise<any>;

  // 종료 시 연결 해제 처리
  static async cleanup(): Promise<void> {
    for (const connector of DBConnector.connectors) {
      try {
        await connector.disconnect();
        log(`${connector.constructor.name} disconnected successfully`);
      } catch (err) {
        error(`Error disconnecting ${connector.constructor.name}:`, err);
      }
    }
  }
}

팩토리 메서드 (index.ts)

export const getConnector = (type: 'postgresql' | 'mysql' | 'oracle', connStr: string, options={}): DBConnector => {
  if (type === 'postgresql') {
    return PostgreSQLConnector.getInstance(connStr, options);
  } else if (type === 'mysql') {
    return MySQLConnector.getInstance(connStr, options);
  } else if (type === 'oracle') {
    return OracleConnector.getInstance(connStr, options);
  } else {
    throw new Error(`Unsupported database type: ${type}`);
  }
};

주요 이점

  • 각 DBMS별 연결 로직 캡슐화
  • 단일 인터페이스로 다양한 데이터베이스 접근 가능
  • 애플리케이션 종료 시 자동으로 모든 연결 해제
  • 새로운 DBMS 추가 시 확장 용이

3. AD/LDAP 활용한 계정 통합

여러 시스템에 분산된 사용자 계정을 중앙 집중식으로 통합했습니다.

주요 구현 내용

  • AD/LDAP 서버와 연동하여 사용자 인증 로직 단일화
  • 권한 관리 체계 통합으로 접근 제어 일관성 확보
  • 부서 이동, 퇴사 등 인사 변동 시 자동 반영

이점

  • 계정 관리 비용 및 보안 리스크 감소
  • 사용자 경험 향상 (단일 계정으로 여러 시스템 접근)
  • 권한 관리 효율성 개선

4. 확장성과 유지보수성을 위한 구조 관리

체계적인 코드 구조로 유지보수 용이성을 확보했습니다.

서버 초기화 코드

const app: express.Express = express();

serverConfig(app);

/**
 * 로깅 미들웨어 설정
 */
app.use(loggerMiddleware);

/**
 * API 라우트 설정
 */
setupRoutes(app);

const port = Number(process.env.PORT);

app.listen(port, ipv4, () => {
  log("server start port : ", port);
});

주요 모듈화 내용

  • config: 환경 설정, 미들웨어 설정 등
  • router: API 엔드포인트 정의 및 컨트롤러 연결
  • controller: 요청 처리 및 비즈니스 로직 조율
  • service: 핵심 비즈니스 로직 구현
  • middleware: 인증, 로깅 등 공통 기능

결론

API 서버 설계를 통해 달성한 핵심 성과:

  • 배포 효율성 향상: 표준화된 프로시저 구조로 배포 횟수 대폭 감소
  • 다양한 DBMS 연동: 팩토리 패턴으로 여러 데이터베이스 통합 관리
  • 계정 관리 일원화: AD/LDAP 기반 사용자 인증으로 보안성 강화
  • 유지보수성 확보: 체계적인 코드 구조로 확장성 및 개발 효율 향상

이러한 API 서버 설계는 전체 통합 시스템의 중추 역할을 담당하며, 향후 시스템 확장이나 변경에도 유연하게 대응할 수 있는 기반을 마련했습니다.

0개의 댓글